اشتراکها
مقایسهای بین RepoDb و Dapper
اشتراکها
تشخیص کدهای sync در ASP.NET Core
اشتراکها
امنیت snapchat
مطالب
آموزش QUnit #2
فریم ورک تست جاوا اسکریپت QUnit:
انتخاب و استفاده از یک فریم ورک برای تست کدهای جاوا اسکریپت، قطعا نتیجه بهتری را به همراه خواهد داشت. من در این جا از QUnit که یکی از بهترینهای تست واحد است، استفاده میکنم. برای این کار فایلهای qunit.js و qunit.css را دانلود و مانند زیر برای تست واحد آماده کنید:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Refactored date examples</title> <link rel="stylesheet" href="../qunit.css"> <script src="../qunit.js"></script> <script src="prettydate.js"></script> <script> test("prettydate basics", function() { var now = "2013/01/28 22:25:00"; equal(prettyDate(now, "2013/01/28 22:24:30"), "just now"); equal(prettyDate(now, "201308/01/28 22:23:30"), "1 minute ago"); equal(prettyDate(now, "2013/01/28 21:23:30"), "1 hour ago"); equal(prettyDate(now, "2013/01/27 22:23:30"), "Yesterday"); equal(prettyDate(now, "2013/01/26 22:23:30"), "2 days ago"); equal(prettyDate(now, "2012/01/26 22:23:30"), undefined); }); </script> </head> <body> <div id="qunit"></div> </body> </html>
در کد بالا ابتدا فایلهای فریم ورک و فایل prettydate.js را اضافه کردیم. برای نمایش نتیجه تست، یک تگ div با نام qunit در بین تگ body اضافه میکنیم.
تابع test:
این تابع برای تست توابع نوشته شده، استفاده میشود. ورودیهای این تابع، یکی عنوان تست و دومی یک متود دیگر، به عنوان ورودی دریافت میکند که در آن بدنه تست نوشته میشود.
تابع equal:
اولین تابع برای سنجش تست واحد equal است و در آن، تابعی که میخواهیم تست کنیم با مقدار خروجی آن مقایسه میشود.
فایل را با نام test.htm ذخیره و آن را در مرورگر خود باز نمایید. خروجی در شکل آورده شده است:
همین طور که در تصویر بزرگ میبینید اطلاعات مرورگر، زمان تکمیل تست و تعداد تست، تعداد تست پاس شده و تعداد تست شکست خورده، نشان داده شده است.
اگر یکی از تستها با شکست روبرو شود رنگ پس زمینه قرمز و جزئیات شکست نمایش داده میشوند.
بهینه سازی، مرحله اول:
در حال حاضر تست ما کامل نیست زیرا امکان تست n weeks ago یا تعداد هفته پیش میسر نیست. قبل از آنکه این را به آزمون اضافه کنیم، تغییراتی در تست میدهیم
test("prettydate basics", function() { function date(then, expected) { equal(prettyDate("2013/01/28 22:25:00", then), expected); } date("2013/01/28 22:24:30", "just now"); date("2013/01/28 22:23:30", "1 minute ago"); date("2013/01/28 21:23:30", "1 hour ago"); date("2013/01/27 22:23:30", "Yesterday"); date("2013/01/26 22:23:30", "2 days ago"); date("2012/01/26 22:23:30", undefined); });
تابع prettyDate را در تابع دیگری به نام date قرار میدهیم. این تغییر سبب میشود تا امکان مقایسه زمان ورودی تست جاری با تست قبلی فراهم شود.
تست دستکاری عناصر DOM:
تا اینجا با تست توایع آشنا شدید، حالا میخواهیم تغییراتی در prettyDate دهیم تا امکان انتخاب عناصر DOM و به روزرسانی آن نیز وجود داشته باشد. فایل prettyDate2.js در زیر آورده شده است:
var prettyDate = { format: function(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"; }, update: function(now) { var links = document.getElementsByTagName("a"); for ( var i = 0; i < links.length; i++ ) { if ( links[i].title ) { var date = prettyDate.format(now, links[i].title); if ( date ) { links[i].innerHTML = date; } } } } };
prettyDate شامل دو تابع، یکی format که weeks ago به آن اضافه گردیده و تابع update که با انتخاب تگها، مقدار title را به تابع فرمت و خروجی آن را در Html هر عنصر قرار میدهد. حال یک تست واحد مینویسیم:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Refactored date examples</title> <link rel="stylesheet" href="../qunit.css"> <script src="../qunit.js"></script> <script src="prettydate2.js"></script> <script> test("prettydate.format", function() { function date(then, expected) { equal(prettyDate.format("2013/01/28 22:25:00", then), expected); } date("2013/01/28 22:24:30", "just now"); date("2013/01/28 22:23:30", "1 minute ago"); date("2013/01/28 21:23:30", "1 hour ago"); date("2013/01/27 22:23:30", "Yesterday"); date("2013/01/26 22:23:30", "2 days ago"); date("2012/01/26 22:23:30", undefined); }); function domtest(name, now, first, second) { test(name, function() { var links = document.getElementById("qunit-fixture") .getElementsByTagName("a"); equal(links[0].innerHTML, "January 28th, 2013"); equal(links[2].innerHTML, "January 27th, 2013"); prettyDate.update(now); equal(links[0].innerHTML, first); equal(links[2].innerHTML, second); }); } domtest("prettyDate.update", "2013-01-28T22:25:00Z", "2 hours ago", "Yesterday"); domtest("prettyDate.update, one day later", "2013/01/29 22:25:00", "Yesterday", "2 days ago"); </script> </head> <body> <div id="qunit"></div> <div id="qunit-fixture"> <ul> <li id="post57"> <p>blah blah blah...</p> <small> Posted <span> <a href="/2013/01/blah/57/" title="2013-01-28T20:24:17Z" >January 28th, 2013</a> </span> by <span><a href=""></a></span> </small> </li> <li id="post57"> <p>blah blah blah...</p> <small> Posted <span> <a href="/2013/01/blah/57/" title="2013-01-27T22:24:17Z" >January 27th, 2013</a> </span> by <span><a href=""></a></span> </small> </li> </ul> </div> </body> </html>
همین طور که مشاهد میکنید در تست واحد اول خود تابع prettyDate.format را تست نموده ایم. در تست بعدی عناصر DOM نیز دستکاری و تست شده است. تابع domtest با جستجوی تگ qunit-fixture و تگهای a درون آن، مقدار نهایی html آن با مقدار داده شده، مقایسه شده است.
در شکل بالا نتیجه تست واحد نشان داده شده است.
در زمان نوشتن تستهای مختلف (Unit - Integration - UI) گاهی اوقات پیش میآید که بخواهید تمامی خصوصیتهای یک شیء را تایید کنید. معمولا نوشتن اعتبارسنجی برای همه خصوصیتها و همین طور پیامهای استثناء برای هر یک در زمان عدم تایید اعتبار، کار بسیار زمانبری است. در این مقاله به شما نشان خواهم داد که چگونه با نوشتن یک اعتبارسنج عمومی از اتلاف زمان زیادی جلوگیری کنید.
با استفاده از کلاس زیر میتوان کار اعتبارسنجی را با استفاده از Reflection به راحتی انجام داد. در اینجا برای اعتبارسنجی DateTime از کلاس DateTimeAssert استفاده کردهایم.
و دو نمونه از آن را ایجاد کرده ایم:
کلاسی را با ارث بری از PropertiesValidator ایجاد میکنیم:
نکته: در صورتی که میخواهید خصوصیتی را استثناء کنید از اعتبارسنجی، میتوانید آنرا به عنوان پارامتر سوم به بعد به تابع Validate ارسال کنید. طبق کد بالا FirstName به صورت استثناء تعریف شده است.
اکنون دو نمونه ساخته شده از ObjectToAssert بالا را با فراخوانی دستور زیر اعتبارسنجی میکنیم:
با استفاده از کلاس زیر میتوان کار اعتبارسنجی را با استفاده از Reflection به راحتی انجام داد. در اینجا برای اعتبارسنجی DateTime از کلاس DateTimeAssert استفاده کردهایم.
public class PropertiesValidator<TK, T> where T : new() where TK : new() { static TK _instance; public static TK Instance { get { if (_instance == null) { _instance = new TK(); } return _instance; } } public void Validate(T expectedObject, T realObject, params string[] propertiesNotToCompare) { var properties = realObject.GetType().GetProperties(); foreach (var currentRealProperty in properties) { if (!propertiesNotToCompare.Contains(currentRealProperty.Name)) { var currentExpectedProperty = expectedObject.GetType().GetProperty(currentRealProperty.Name); var exceptionMessage = $"The property {currentRealProperty.Name} of class {currentRealProperty.DeclaringType?.Name} was not as expected."; if (currentRealProperty.PropertyType != typeof(DateTime) && currentRealProperty.PropertyType != typeof(DateTime?)) { Assert.AreEqual( currentExpectedProperty.GetValue( expectedObject, null ), currentRealProperty.GetValue( realObject, null ), exceptionMessage ); } else { DateTimeAssert.Validate( currentExpectedProperty.GetValue( expectedObject, null ) as DateTime?, currentRealProperty.GetValue( realObject, null ) as DateTime?, TimeSpan.FromMinutes( 5 ) ); } } } } }
طرز استفاده
فرض کنید مدلی داریم با این مشخصات:public class ObjectToAssert { public string FirstName { get; set; } public string LastName { get; set; } public DateTime LastVisit { get; set; } }
var expectedObject = new ObjectToAssert { FirstName = "Vahid", LastName = "Mohammad Taheri", LastVisit = new DateTime( 2016, 11, 14, 0, 10, 50 ) }; var actualObject = new ObjectToAssert { FirstName = "Vahid", LastName = "Mohammad Taheri", LastVisit = new DateTime( 2016, 11, 14, 0, 13, 50 ) };
public class ObjectToAssertValidator : PropertiesValidator<ObjectToAssertValidator, ObjectToAssert> { public void Validate(ObjectToAssert expected, ObjectToAssert actual) { this.Validate(expected, actual, "FirstName"); } }
نکته: در صورتی که میخواهید خصوصیتی را استثناء کنید از اعتبارسنجی، میتوانید آنرا به عنوان پارامتر سوم به بعد به تابع Validate ارسال کنید. طبق کد بالا FirstName به صورت استثناء تعریف شده است.
اکنون دو نمونه ساخته شده از ObjectToAssert بالا را با فراخوانی دستور زیر اعتبارسنجی میکنیم:
ObjectToAssertValidator.Instance.Validate(expectedObject, actualObject);