مقدمه:
تست و آزمایش کد برنامهها و وب سایتهایمان، بهترین راه کاهش خطا و مشکلات آنها بعد از انتشار است. از جمله روشهای موجود، تست واحد است که ویژوال استادیو نیز از آن برای پروژههای دات نت پشتیبانی میکند. با افزایش روز افزون کتابخانههای جاوا اسکریپتی و جی کوئری، نیاز به تست کدهای جاواسکریپتی نیز بیشتر به نظر میرسد و بهتر است تست واحد و آزمایش شوند. اما برخلاف کدهای #C و ASP.NET تست کدهای جاوا اسکریپت، مخصوصا زمانی که به دستکاری عناصر DOM میپردازیم و یا رویدادهای درون صفحه وب را با استفاده از جی کوئری مینویسیم، حتی اگر در فایل جداگانهای نوشته شود، این بدان معنی نیست که آماده تست واحد است و ممکن است امکان نوشتن تست وجود نداشته باشد.
بنابراین چه چیزی یک تست واحد است؟ در بهترین حالت توابعی که مقداری را برمی گردادنند، بهترین حالت برای تست واحد است. اما در بیشتر موارد شما نیاز دارید تا تاثیر کد را بر روی عناصر صفحه نیز مشاهد نمایید.
ساخت تست واحد
برای تست پذیری بهتر، توابع جاوا اسکریپت و هر کد دیگری، آن را میبایست طوری بنویسید که مقادیر تاثیر گذار در اجرای تابع به عنوان ورودی تابع در نظر گرفته شده باشند و همیشه نتیجه به عنوان خروجی تابع برگردانده شود؛ قطعه کد زیر را در نظر بگیرید:
function prettyDate(time){
var date = new Date(time || ""),
diff = (((new Date()).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
return;
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor( diff / 60 ) +
" minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor( diff / 3600 ) +
" hours ago") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) +
" weeks ago";
}
تابع pertyDate اختلاف زمان حال را نسبت به زمان ورودی، بصورت یک رشته برمی گرداند. اما در اینجا مقدار زمان حال، در خط سوم، در خود تابع ایجاد شده است و در صورتی که بخواهیم برای چندین مقدار آن را تست کنیم زمان حال متفاوتی در نظر گرفته میشود و حداکثر، زمان 31 روز قبل را نمایش داده و در بقیه تاریخ ها undefined را بر میگرداند. برای تست واحد، چند تغییر میدهیم.
بهینه سازی، مرحله اول:
پارامتری به عنوان مقدار زمان جاری برای تابع در نظر میگیریم و تابع را جدا کرده و در یک فایل جداگانه قرار میدهیم. فایل prettydate.js بصورت زیر خواهد شد.
function prettyDate(now, time){
var date = new Date(time || ""),
diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
return;
return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute ago" ||
diff < 3600 && Math.floor( diff / 60 ) +
" minutes ago" ||
diff < 7200 && "1 hour ago" ||
diff < 86400 && Math.floor( diff / 3600 ) +
" hours ago") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days ago" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) +
" weeks ago";
}
حال یک تابع برای تست داریم، چند تست واحد واقعی مینویسیم
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Refactored date examples</title>
<script src="prettydate.js"></script>
<script>
function test(then, expected) {
results.total++;
var result = prettyDate("2013/01/28 22:25:00", then);
if (result !== expected) {
results.bad++;
console.log("Expected " + expected +
", but was " + result);
}
}
var results = {
total: 0,
bad: 0
};
test("2013/01/28 22:24:30", "just now");
test("2013/01/28 22:23:30", "1 minute ago");
test("2013/01/28 21:23:30", "1 hour ago");
test("2013/01/27 22:23:30", "Yesterday");
test("2013/01/26 22:23:30", "2 days ago");
test("2012/01/26 22:23:30", undefined);
console.log("Of " + results.total + " tests, " +
results.bad + " failed, " +
(results.total - results.bad) + " passed.");
</script>
</head>
<body>
</body>
</html>
در کد بالا یک تابع بدون استفاده از Qunit برای تست واحد نوشته ایم که با آن تابع prettyDate را تست میکند. تابع test مقدار زمان حال و رشته خروجی را گرفته و آن را با تابع اصلی تست میکند در آخر تعداد تست ها، تستهای شکست خورده و تست های پاس شده گزارش داده میشود.
خروجی میتواند مانند زیر باشد:
Of 6 tests, 0 failed, 6 passed.
Expected 2 day ago, but was 2 days ago.
f 6 tests, 1 failed, 5 passed.