🔸 الگوهای طراحی راه
حلهای معمولی برای مشکلات رایج در طراحی نرمافزار هستند. آنها مانند
نقشههای از پیش ساخته شدهای هستند که میتوانید آنها را برای حل یک مشکل
طراحی تکراری در کد خود سفارشی کنید.
🔸اینطور نیست که مثلا یک الگو
را پیدا کنید و آن را در برنامه خود کپی کنید. الگو، یک قطعه کد خاص نیست،
بلکه یک مفهوم کلی برای حل یک مشکل خاص است. شما میتوانید جزئیات الگو را
دنبال کنید و راه حلی متناسب با واقعیتهای برنامه خود را پیادهسازی
کنید.
🔸 الگوها اغلب با الگوریتمها اشتباه گرفته میشوند، زیرا هر
دو مفهوم راه حلهای معمولی برای برخی از مسائل شناخته شده را توصیف
میکنند. در حالی که یک الگوریتم همیشه مجموعه واضحی از اقدامات را تعریف
میکند که میتواند به هدفی دست یابد، یک الگو راه حلهای سطح بالا برای
مسائل سطح بالا هستند. کد یک الگوی اعمال شده برای دو برنامه مختلف ممکن
است متفاوت باشد.
🔸 همیشه منظور هر الگو را در ذهن خود مرور کنید و هنگام برخورد با یک مسئله به دنبال مناسبترین الگو بگردید.
🔸 شما نمیتوانید یک الگو را به کار بگیرید مگر آنکه آن را به خوبی فهمیده باشید. بنابراین در اولین گام باید اصول و الگوهای طراحی را هم به شکل انتزاعی و هم به شکل واقعی خوانده و تمرین کنید. دقت کنید که
یک الگو را به شکلهای مختلفی میتوان پیادهسازی کرد. هر چه پیاده
سازیهای بیشتری ببینید، به هدف و چگونگی استفاده از آن بهتر مسلط
میشوید.
راهنمای شروع سریع برای مطالعه الگوهای طراحی 👇🏻
PDF Cards: designpatternscard.pdf
DesignPatterns-online.pdf
مفاهیم برنامه نویسی ـ مروری بر کلاس و شیء
پراید به عنوان رده ای از اتومبیلها کلاس است. اما اگر به کسی یک اتومبیل پراید در حال عبور را نشان دهید که دارای پلاک و ... است، آن شیء ای است از کلاس پراید. اگر در یک پارکینگ تعدادی اتومبیل باشد و از کسی بخواهیم بر اساس نوع گروه بندی یا کلاس بندی کند، بعد از چند دقیقه خواهیم دید اتومبیلهای هم نوع را جدا کرده. مثلا همه اتومبیلهای از نوع پراید را کنار هم قرار داده. این همان مفهوم کلاس بندی است. برای تولید اتومبیلی از نوع پراید کارخانه یک نقشه یا طرحی ایجاد میکند که بسیاری مشخصات و چگونگی ساخت را در خود دارد. چگونگی انجام برخی کارکردها مانند حرکت را دارد. این طرح کلاس نامیده میشود. اما آیا میتوان برای آن پلاک در نظر گرفت؟ خیر چون فقط نقشه ایجاد اتومبیل است (یا به عبارتی فقط مفهوم است). همچنین کلاس پراید احتمالاً از کلاس اتومبیل یا ماشین برخی خصوصیات و رفتارها را با ارث برده است.
بیاد داشته باشیم هر فردی در هر سطحی از دانش هم مسلماً بسیاری چیزها را هنوز نمیداند و دانستههای ما در مقابل نادانستهها قطره ای بیش نیست. تا جایی که میتوان گفت همه ما از نظر نادانستهها برابریم. تفاوت در دانسته هاست. کسانی که سالها برنامه نویسی میکنند هم حتما دانسته هایی دارند که میتوانند این کار را ادامه دهند. پس کافی است آنچه نمیدانند را سعی کنیم به اشتراک بگذاریم تا بدانند و از دانسته هایشان استفاده کنیم.
550-5.7.1+This+message+does+not+have+authentication+information+or+fails+to+pass
SPF چیست؟
SPF یا Sender Framework Policy، رکوردهای مخصوص DNS ای هستند که مشخص میکنند کدام میل سرورها، بر اساس نام دومین جاری، مجاز هستند ایمیل ارسال کنند. برای مثال اگر کلاینتی با IP1 سعی کند خودش را بجای دومین شما با IP0، معرفی کند و ایمیل ارسال کند، ایمیلهای او برگشت خواهند خورد. در این حالت این کلاینت، پیامهای خطایی شروع شدهی با عبارات زیر را دریافت خواهد کرد:
550-5.7.1 This message does not have authentication information or fails to pass 550-5.7.1 SPF Failed validation status=bounced (host gmail-smtp-in.l.google.com said: 550-5.7.1 This message does not have authentication information or fails to pass 550-5.7.1 authentication checks. To best protect our users from spam, the 550-5.7.1 message has been blocked. Please visit 550-5.7.1 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.1 information.
چگونه باید SPF را تنظیم کرد؟
برای این منظور نیاز است به پنل تنظیمات دومین خریداری شدهی خود دسترسی داشته باشید و بتوانید در آنجا یک DNS Record جدید از نوع TXT را اضافه کنید؛ با این مشخصات:
Name/Host/Alias: @ Time to Live (TTL): 3600 or leave the default Value/Answer/Destination: v=spf1 ip4:xx.xx.xx.xx include:_spf.google.com ~all
چگونه بررسی کنیم که رکورد SPF دومین ما به درستی تنظیم شدهاست؟
برای این منظور میتوان از ابزار Check MX گوگل، استفاده کرد:
https://toolbox.googleapps.com/apps/checkmx/check
[TestMethod] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).TypeText("WatiN"); browser.Button(Find.ByName("btnG")).Click(); Assert.IsTrue(browser.ContainsText("WatiN")); } }
[TestMethod] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).Value="WatiN"; browser.Button(Find.ByName("btnG")).ClickNoWait(); Thread.Sleep(3000); Assert.IsTrue(browser.ContainsText("WatiN")); } }
به صورت پیش فرض تنظیمات حافظهی اس کیوال سرور به صورتی است که به آن اجازه میدهد تمامی حافظهی مهیای سیستم عامل را مصرف کند! اگر شخصی با این مساله آشنایی نداشته باشد احتمالا تصور خواهد کرد که اس کیوال سرور نشتی حافظه دارد یا کلا مشکلی در سیستم روی داده است که تا این حد مصرف حافظه بالا رفته است.
برای مشاهدهی این تنظیمات روی instance سرور مورد نظر کلیک راست کرده و گزینه خواص را انتخاب کنید. سپس در صفحهی باز شده گزینه memory را مشاهده نمائید.
البته اینکار را با استفاده از دستورات T-SQL هم میتوان انجام داد:
EXEC sp_configure 'show advanced options', 1
RECONFIGURE WITH OVERRIDE
EXEC sp_configure 'max server memory (MB)'
EXEC sp_configure 'show advanced options', 0
RECONFIGURE WITH OVERRIDE
که البته عمدا آنرا بر روی یک گیگ تنظیم کردهام تا عملکرد سیستم مختل نشود. (چون اگر غیر از این باشد، تعارفی نداشته و هر آنچه را که صلاح بداند مصرف میکند!)
کمی در بلاگهای رسمی برنامه نویسهای اس کیوال سرور در سایت MSDN در اینباره جستجو کردم و نتیجه آن به صورت زیر است:
تنظیمات حداکثر حافظهی مصرفی اس کیوال سرورهای 2005 و 2008
برای یک سیستم عامل 64 بیتی:
Physical RAM | MaxMem Setting |
2GB | 1500 |
4GB | 3200 |
6GB | 4800 |
8GB | 6700 |
12GB | 10600 |
16GB | 14500 |
24GB | 22400 |
32GB | 30000 |
48GB | 45000 |
64GB | 59000 |
برای سروری 32 بیتی این اعداد حداکثر را تقریبا منهای 300 مگابایت نمائید. (و البته همانطور که مطلع هستید در سیستم عاملهای سرور 32 بیتی، اگر بیشتر از 2 گیگا بایت رم داشتید سوئیچ 3GB و اگر بیشتر از 4 گیگابایت رم مهیا بود، سوئیچ PAE باید در boot.ini تنظیم شوند. در غیر اینصورت هزینهی سخت افزاری بیهودهای را متحمل شدهاید، زیرا استفادهی بهینهای از آن در یک سیستم عامل 32 بیتی نخواهد شد)
و این اعداد را همانطور که ذکر شد، در تنظیمات سرور میتوان وارد نمود ( از طریق management studio ) و یا با استفاده از دستورات T-SQL نیز این امر میسر است: (در مثال زیر حداکثر حافظه مجاز مصرفی به 2300 مگابایت تنظیم میشود)
EXEC sp_configure 'show advanced options', 1
RECONFIGURE WITH OVERRIDE
EXEC sp_configure 'max server memory (MB)', 2300
EXEC sp_configure 'show advanced options', 0
RECONFIGURE WITH OVERRIDE
برای مطالعهی بیشتر
http://sqlnerd.blogspot.com/2006/07/memory-use-in-sql-server.html
http://blogs.msdn.com/axperf/archive/2008/03/10/welcome-database-configuration-checklist-part-1.aspx
http://technet.microsoft.com/en-us/library/bb124810.aspx
دریافت کتابخانه DNT Scheduler و مثال آن
DNTScheduler
در این بسته، کدهای کتابخانهی DNT Scheduler و یک مثال وب فرم را، ملاحظه خواهید کرد. از این جهت که برای ثبت وظایف این کتابخانه، از فایل global.asax.cs استفاده میشود، اهمیتی ندارد که پروژهی شما وب فرم است یا MVC. با هر دو حالت کار میکند.
نحوهی تعریف یک وظیفهی جدید
کار با تعریف یک کلاس و پیاده سازی ScheduledTaskTemplate شروع میشود:
public class SendEmailsTask : ScheduledTaskTemplate
using System; namespace DNTScheduler.TestWebApplication.WebTasks { public class SendEmailsTask : ScheduledTaskTemplate { /// <summary> /// اگر چند جاب در یک زمان مشخص داشتید، این خاصیت ترتیب اجرای آنها را مشخص خواهد کرد /// </summary> public override int Order { get { return 1; } } public override bool RunAt(DateTime utcNow) { if (this.IsShuttingDown || this.Pause) return false; var now = utcNow.AddHours(3.5); return now.Minute % 2 == 0 && now.Second == 1; } public override void Run() { if (this.IsShuttingDown || this.Pause) return; System.Diagnostics.Trace.WriteLine("Running Send Emails"); } public override string Name { get { return "ارسال ایمیل"; } } } }
- متد RunAt ثانیهای یکبار فراخوانی میشود (بنابراین بررسی now.Second را فراموش نکنید). زمان ارسالی به آن UTC است و اگر برای نمونه میخواهید بر اساس ساعت ایران کار کنید باید 3.5 ساعت به آن اضافه نمائید. این مساله برای سرورهایی که خارج از ایران قرار دارند مهم است. چون زمان محلی آنها برای تصمیم گیری در مورد زمان اجرای کارها مفید نیست.
در متد RunAt فرصت خواهید داشت تا منطق زمان اجرای وظیفهی جاری را مشخص کنید. برای نمونه در مثال فوق، این وظیفه هر دو دقیقه یکبار اجرا میشود. یا اگر خواستید اجرای آن فقط در سال 23 و 33 دقیقه هر روز باشد، تعریف آن به نحو ذیل خواهد بود:
public override bool RunAt(DateTime utcNow) { if (this.IsShuttingDown || this.Pause) return false; var now = utcNow.AddHours(3.5); return now.Hour == 23 && now.Minute == 33 && now.Second == 1; }
خاصیت Pause هر وظیفه را برنامه میتواند تغییر دهد. به این ترتیب در مورد توقف یا ادامهی یک وظیفه میتوان تصمیم گیری کرد. خاصیت ScheduledTasksCoordinator.Current.ScheduledTasks، لیست وظایف تعریف شده را در اختیار شما قرار میدهد.
- در متد Run، منطق وظیفهی تعریف شده را باید مشخص کرد. برای مثال ارسال ایمیل یا تهیهی بک آپ.
- Name نیز نام وظیفهی جاری است که میتواند در گزارشات مفید باشد.
همین مقدار برای تعریف یک وظیفه کافی است.
نحوهی ثبت و راه اندازی وظایف تعریف شده
پس از اینکه چند وظیفه را تعریف کردیم، برای مدیریت بهتر آنها میتوان یک کلاس ثبت و معرفی کلی را مثلا به نام ScheduledTasksRegistry ایجاد کرد:
using System; using System.Net; namespace DNTScheduler.TestWebApplication.WebTasks { public static class ScheduledTasksRegistry { public static void Init() { ScheduledTasksCoordinator.Current.AddScheduledTasks( new SendEmailsTask(), new DoBackupTask()); ScheduledTasksCoordinator.Current.OnUnexpectedException = (exception, scheduledTask) => { //todo: log the exception. System.Diagnostics.Trace.WriteLine(scheduledTask.Name + ":" + exception.Message); }; ScheduledTasksCoordinator.Current.Start(); } public static void End() { ScheduledTasksCoordinator.Current.Dispose(); } public static void WakeUp(string pageUrl) { try { using (var client = new WebClient()) { client.Credentials = CredentialCache.DefaultNetworkCredentials; client.Headers.Add("User-Agent", "ScheduledTasks 1.0"); client.DownloadData(pageUrl); } } catch (Exception ex) { //todo: log ex System.Diagnostics.Trace.WriteLine(ex.Message); } } } }
- توسط متد ScheduledTasksCoordinator.Current.AddScheduledTasks، تنها کافی است کلاسهای وظایف مشتق شده از ScheduledTaskTemplate، معرفی شوند.
- به کمک متد ScheduledTasksCoordinator.Current.Start، کار Thread timer برنامه شروع میشود.
- اگر در حین اجرای متد Run، استثنایی رخ دهد، آنرا توسط یک Action delegate به نام ScheduledTasksCoordinator.Current.OnUnexpectedException میتوانید دریافت کنید. کتابخانهی DNT Scheduler برای اجرای وظایف، از یک ترد با سطح تقدم Below normal استفاده میکند تا در حین اجرای وظایف، برنامهی جاری با اخلال و کندی مواجه نشده و بتواند به درخواستهای رسیده پاسخ دهد. در این بین اگر استثنایی رخ دهد، میتواند کل پروسهی IIS را خاموش کند. به همین جهت این کتابخانه کار try/catch استثناهای متد Run را نیز انجام میدهد تا از این لحاظ مشکلی نباشد.
- متد ScheduledTasksCoordinator.Current.Dispose کار مدیر وظایف برنامه را خاتمه میدهد.
- از متد WakeUp تعریف شده میتوان برای بیدار کردن مجدد برنامه استفاده کرد.
استفاده از کلاس ScheduledTasksRegistry تعریف شده
پس از اینکه کلاس ScheduledTasksRegistry را تعریف کردیم، نیاز است آنرا به فایل استاندارد global.asax.cs برنامه به نحو ذیل معرفی کنیم:
using System; using System.Configuration; using DNTScheduler.TestWebApplication.WebTasks; namespace DNTScheduler.TestWebApplication { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { ScheduledTasksRegistry.Init(); } protected void Application_End() { ScheduledTasksRegistry.End(); //نکته مهم این روش نیاز به سرویس پینگ سایت برای زنده نگه داشتن آن است ScheduledTasksRegistry.WakeUp(ConfigurationManager.AppSettings["SiteRootUrl"]); } } }
- متد ScheduledTasksRegistry.End در پایان کار برنامه جهت پاکسازی منابع باید فراخوانی گردد.
همچنین در اینجا با فراخوانی ScheduledTasksRegistry.WakeUp، میتوانید برنامه را مجددا زنده کنید! IIS مجاز است یک سایت ASP.NET را پس از مثلا 20 دقیقه عدم فعالیت (فعالیت به معنای درخواستهای رسیده به سایت است و نه کارهای پس زمینه)، از حافظه خارج کند (این عدد در application pool برنامه قابل تنظیم است). در اینجا در فایل web.config برنامه میتوانید آدرس یکی از صفحات سایت را برای فراخوانی مجدد تعریف کنید:
<?xml version="1.0"?> <configuration> <appSettings> <add key="SiteRootUrl" value="http://localhost:10189/Default.aspx" /> </appSettings> </configuration>
گزارشگیری از وظایف تعریف شده
برای دسترسی به کلیه وظایف تعریف شده، از خاصیت ScheduledTasksCoordinator.Current.ScheduledTasks استفاده نمائید:
var jobsList = ScheduledTasksCoordinator.Current.ScheduledTasks.Select(x => new { TaskName = x.Name, LastRunTime = x.LastRun, LastRunWasSuccessful = x.IsLastRunSuccessful, IsPaused = x.Pause, }).ToList();
معماری لایه بندی نرم افزار #3
- پیاده سازی الگوی مخزن در عمل (بر اساس بحث فعلی که در مورد کار با ORMها است) به صورت کپسوله سازی ORM در همه جا مطرح میشه و اینکار اساسا اشتباه هست. چون هم شما رو محروم میکنه از قابلیتهای پیشرفته ORM و هم ارزش افزودهای رو به همراه نداره. دست آخر میبینید در لایه مخزن GetAll دارید در لایه سرویس هم GetAll دارید. این مساله هیچ مزیتی نداره. یک زمانی در ADO.NET برای GetAll کردن باید کلی کد شبیه به کدهای یک ORM نوشته میشد. خود ORM الان اومده اینها رو کپسوله کرده و لایهای هست روی اون. اینکه ما مجددا یک پوسته روی این بکشیم حاصلی نداره بجز تکرار کد. عدهای عنوان میکنند که حاصل اینکار امکان تعویض ORM رو ممکن میکنه ولی اینها هم بعد از یک مدت تجربه با ORMهای مختلف به این نتیجه میرسند که ای بابا! حتی پیاده سازی LINQ این ORMها یکی نیست چه برسه به قابلیتهای پیشرفتهای که در یکی هست در دوتای دیگر نیست (واقع بینی، بجای بحث تئوری محض).
- اینکه این تبدیلات (پر کردن ViewModel از روی مدل) هم میتونه و بهتره که (نه الزاما) در لایه سرویس انجام بشه، نتیجه مناسبی هست.
کاربردهای Static reflection - قسمت اول
در «یک میلیون بار» اجرا، حدودا 10 ثانیه تفاوت اجرا است نسبت به حالت بکارگیری رشتهها.
البته شما در عمل، نه در محیط آزمایشگاهی، پیدا کنید برنامهای را که یک میلیون بار بخواهد خواصی را مرتبا به روز کند.
- زمانیکه LINQ هم ارائه شد، اولین مقالاتی که در این مورد ... در مورد نقد آن منتشر شد، تمرکز را گذاشتند روی کارآیی؛ که این کمی کند است! البته الان کمتر کسی است که در پروژههایش حداقل از LINQ to Objects استفاده نکند. به این دلایل:
- هدف استفاده از LINQ اصلا مسابقهی سرعت نیست.
- هدف تولید کدهای Strongly typed که این اهمیتها را دارند: تحت نظر کامپایلر هستند، قابلیت refactoring دارند و intellisense خودکاری را به همراه خواهند داشت. تمام اینها نگهداری یک پروژه را (که اصل زمان اختصاص داده شده به توسعه یک نرم افزار هم همین قسمت نگهداری است)، سادهتر و قابل تحملتر میکند.
- کاهش حجم کدهای نوشته شده. شما میتونید حجم بالایی از if-else و for و حلقهها و غیره رو با یک سطر LINQ نمایش بدید. این هم در بالابردن خوانایی و همچنین نگهداری سادهتر برنامه مؤثر است.
- تبدیل سادهتر اطلاعات خام به اشیاء (LINQ to xyz ها)
و ...
شما خیلی از مزایا رو بدست خواهید آورد اما خوب مسلما اینها هزینه هم دارند. اما نه آنچنان که کسی بخواهد از آنها صرفنظر کند.
چندی پیش در سایت جاری چند مقاله خوب توسط یکی از دوستان درباره Qunit منتشر شد. Qunit یک ابزار قدرتمند و مناسب برای تست کدهای جاوااسکریپت است و در اثبات صحت این گفته همین کافیست که بدانیم برای تست کدهای نوشته شده در پروژههای متن بازی هم چون Backbone.Js و JQuery از این فریم ورک استفاده شده است. اما به احتمال قوی در ذهن شما این سوال مطرح شده است که خب! در صورت آشنایی با Qunit چه نیاز به یادگیری Jasmine یا خدای نکرده Mocha و FuncUnit است؟ هدف صرفا معرفی یک ابزار غیر برای تست کد است نه مقایسه و نتیجه گیری برای تعیین میزان برتری این ابزارها. اصولا مهمترین دلیل برای انتخاب، علاوه بر امکانات و انعطاف پذیری، فاکتور راحتی و آسان بودن در هنگام استفاده است که به صورت مستقیم به شما و تیم توسعه نرم افزار بستگی دارد.
اما به عنوان توسعه دهنده نرم افزار که قرار است از این ابزار استفاده کنیم بهتر است با تفاوتها و شباهتهای مهم این دو فریم ورک آشنا باشیم:
»Jasmine یک فریم ورک تست کدهای جاوا اسکریپ بر مبنای Behavior-Driven Development است در حالی که Qunit بر مبنای Test-Driven Development است و همین مسئله مهمترین تفاوت بین این دو فریم ورک میباشد.
»اگر قصد دارید که از Qunit نیز به روش BDD استفاده نمایید باید از ترکیب Pavlov به همراه Qunit استفاده کنید.
»Jasmine از مباحث مربوط به Spies و Mocking به خوبی پشتیبانی میکند ولی این امکان به صورت توکار در Qunit فراهم نیست. برای اینکه بتوانیم این مفاهیم را در Qunit پیاده سازی کنیم باید از فریم ورکهای دیگر نظیر SinonJS به همراه Qunit استفاده کنیم.
»هر دو فریم ورک بالا به سادگی و راحتی کار معروف هستند
»تمام موارد مربوط به الگوهای Matching در هر دو فریم ورک به خوبی تعبیه شده است
» هر دو فریم ورک بالا از مباحث مربوط به Asynchronous Testing برای تست کدهای Ajax ای به خوبی پشتیبانی میکنند.
بررسی چند مفهوم
قبل از شروع، بهتر است که با چند مفهوم کلی و در عین حال مهم این فریم ورک آشنا شویم
describe('JavaScript addition operator', function () { it('adds two numbers together', function () { expect(1 + 2).toEqual(3); }); });
در تابع it کد بالا شما میتوانید کدهای مربوط بدنه توابع تست خود را بنویسید. برای پیاده سازی Assert در توابع تست مفهوم expectationها وجود دارد. در واقع expect برای بررسی مقادیر حقیقی با مقادیر مورد انتظار مورد استفاده قرار میگیرد و شامل مقادیر true یا false خواهد بود.
برای Setup و Teardown توابع تست خود باید از توابع beforeEach و afterEach که بدین منظور تعبیه شده اند استفاده کنید.
describe("A spec (with setup and tear-down)", function() { var foo; beforeEach(function() { foo = 0; foo += 1; }); afterEach(function() { foo = 0; }); it("is just a function, so it can contain any code", function() { expect(foo).toEqual(1); }); it("can have more than one expectation", function() { expect(foo).toEqual(1); expect(true).toEqual(true); }); });
اگر در کد تست خود قصد دارید که یک تابع describe یا it را غیر فعال کنید کافیست یک x به ابتدای آنها اضافه کنید و دیگر نیاز به هیچ کار اضافه دیگری برای comment کردن کد نیست.
xdescribe("A spec", function() { var foo; beforeEach(function() { foo = 0; foo += 1; }); xit("is just a function, so it can contain any code", function() { expect(foo).toEqual(1); }); });
درادامه قصد پیاده سازی یک مثال را با استفاده از Jasmine و RequireJs در پروژه Asp.Net MVC دارم.
- فولدر lib شامل فایلها کدهای Jasmine برای setup و tear down و spice و تست کدهای شما میباشد.
- فایل specRunner.html به واقع یک فایل برای نمایش فایلهای تست و همچنین نمایش نتیجه تست است.
- فولدر spec نیز شامل کدهای Jasmine برای کمک به نوشتن تست میباشد.
در این مثال قصد داریم فایلهای player.js و song.js که به عنوان نمونه به همراه این فریم ورک قرار دارد را در قالب یک پروژه MVC به همراه RequireJs، تست نماییم. در نتیجه این فایلها را از فولدر src انتخاب نمایید و آنها را در قسمت Scripts پروژه اصلی خود کپی کنید(ابتدا بک پوشه به نام App بسازید و فایلها را در آن قرار دهید)
برای استفاده از requireJs باید دستور define را در ابتدا این فایلها اضافه نماییم. در نتیجه فایلهای Player.js و Song.js را باز کنید و تغییرات زیر را در ابتدای این فایلها اعمال نمایید.
Song.js
define(function () { function Song() { } Song.prototype.persistFavoriteStatus = function (value) { // something complicated throw new Error("not yet implemented"); }; });
define(function () { function Player() { } Player.prototype.play = function (song) { this.currentlyPlayingSong = song; this.isPlaying = true; }; Player.prototype.pause = function () { this.isPlaying = false; }; Player.prototype.resume = function () { if (this.isPlaying) { throw new Error("song is already playing"); } this.isPlaying = true; }; Player.prototype.makeFavorite = function () { this.currentlyPlayingSong.persistFavoriteStatus(true); }; });
baseUrl در پیکر بندی requireJs به مسیر فایلهای پروژه که در پروژه اصلی MVC قرار دارد اشاره میکند. paths برای تعیین مسیر فایلهای تست که در پوشه spec در پروژه تست قرار دارد اشاره میکند. اگر دقت کرده باشید به دلیل اینگه تگهای script مربوط به لود فایلهای SpecHelper.js و PlayerSpec.js به صورت comment در آمده اند در نتیجه این فایلها لود نخواهند شد و خروجی مورد نظر مشاهده نمیشود. در این جا باید از مکانیزم AMD موجود در RequireJs استفاده نماییم و فایلهای مربوطه را لود کنیم. برای این کار نیاز به اضافه کردن دستور require در ابتدای تگ script به صورت زیر در این فایل است. در نتیجه فایلهای PlayerSpec و SpecHelper نیز توسط RequireJs لود خواهند شد.
نیاز به یک تغییر کوچک دیگر نیز وجود دارد. فایل PlayerSpec را باز نمایید و وابستگی فایلهای آن را تعیین نمایید. از آن جا که این فایل برای تست فایلهای Player , Song ایجاد شده است در نتیجه باید از define برای تعیین این وابستگیها استفاده نماییم.
یادآوری:
»دستور describe در فایل بالا برای تعریف تابع تست است. همان طور که میبینید بک نام به آن داده میشود به همراه بدنه تابع تست.
»دستور beforeEach برای آماده سازی مواردی است که قصد داریم در تست مورد استفاده قرار گیرند. همانند متدهای Setup در UnitTest.
» دستور expect نیز معادل Assert در UnitTest است و برای بررسی صحت عملکرد تست نوشته میشود.
اگر
فایل SpecRunner.html را دوباره در مرورگر خود باز نمایید تصویر زیر را
مشاهده خواهید کرد که به عنوان موفقیت آمیز بودن پیکر بندی پروژه و تستهای آن میباشد.