برای استفاده سادهتر از ابزارهای unit testing در ویژوال استودیو افزونههای زیادی وجود دارند، از ری شارپر تا CodeRush تا حتی امکانات نسخهی کامل VS.NET که با MSTest یکپارچه است. اما اگر نخواهیم از MSTest استفاده کنیم و همچنین افزونهها را هم بخواهیم حذف کنیم (مثلا از نسخهی رایگان express استفاده کنیم)، چطور؟
برای حل این مشکل چندین روش وجود دارد. یا میشود از test runner اینها استفاده کرد که اصلا نیازی به IDE ندارند و مستقل است؛ یا میتوان به صورت زیر هم عمل کرد:
به خواص پروژه در VS.NET مراجعه کنید. برگهی Build events را باز کنید. در اینجا میخواهیم post-build event را مقدار دهی کنیم. به این معنا که پس از هر build موفق، لطفا این دستورات خط فرمان را اجرا کن.
NUnit به همراه test runner خط فرمان هم ارائه میشود و نام آن nunit-console.exe است. اگر به محل نصب آن مراجعه کنید، عموما در آدرس C:\Program Files\NUnit xyz\bin\nunit-console.exe قرار دارد. برای استفاده از آن تنها کافی است تنظیم زیر صورت گیرد:
c:\path\nunit-console.exe /nologo $(TargetPath)
TargetPath به صورت خودکار با نام اسمبلی جاری پروژه در زمان اجرا جایگزین میشود.
اکنون پس از هر Build، به صورت خودکار nunit-console.exe اجرا شده، اسمبلی برنامه که حاوی آزمونهای واحد است به آن ارسال گردیده و سپس خروجی کار در output window نمایش داده میشود. اگر خطایی هم رخ داده باشد در قسمت errors قابل مشاهده خواهد بود.
در اینجا حتی بجای برنامه کنسول یاده شده میتوان از برنامه nunit.exe هم استفاده کرد. در این حالت GUI اصلی پس از هر Build نمایش داده میشود:
c:\path\nunit.exe $(TargetPath)
چند نکته:
1- برنامه nunit-console.exe چون در حال حاضر برای دات نت 2 کامپایل شده امکان بارگذاری dll های دات نت 4 را ندارد. به همین منظور فایل nunit-console.exe.config را باز کرده و تنظیمات زیر را به آن اعمال کنید:
<configuration>
<startup>
<supportedRuntime version="v4.0.30319" />
</startup>
و همچنین:
<runtime>
<loadFromRemoteSources enabled="true" />
2- خروجی نتایج اجرای آزمونها را به صورت XML هم میتوان ذخیره کرد. مثلا:
c:\path\nunit-console.exe /xml:$(ProjectName)-tests.xml /nologo $(TargetPath)
3- از فایل xml ذکر شده میتوان گزارشات زیبایی تهیه کرد. برای مثال:
Generating Report for NUnit
NUnit2Report Task
جهت مطالعه بیشتر:
Setting up Visual C#2010 Express with NUnit
Use Visual Studio's Post-Build Events to Automate Unit Testing Running
3 Ways to Run NUnit From Visual Studio
در پنجره ای که باز میشود ، لیست را نام گذاری میکنیم
سپس یک View ایحاد میکنیم . برای این کار از نوار Ribbon بالا ، روی List View کلیک میکینم :
سپس نامی برای این View در نظر میگیریم و این Viewرا به عنوان نمای پیش فرض انتخاب میکنیم
در این مرحله نمای نگاهی به لیست در مرور گر میاندازیم و تعدادی داده برای آزمایش در آن وارد میکنیم
حال برای اعمال تغییرات به SPD برمیگردیم و روی View ساخته شده کلیک میکنیم تا وارد محیط ویرایش آن شویم .
در این مثال میخواهم روی فیلد نام شرکت فیلتر انجام دهم
سپس برای تغییر نحوه نمایش ، روی Set Style کلیک میکنیم :
پس از اعمال تغییرات میتوانید تغییرات را در کد مشاهده کنید
برای اعمال تغییرات در خروجی و ستونهای آن ، روی Add/Remove Columns کلیک میکنیم :
برای مثال در اینجا ستون Job Title را به لیست اضافه کردم :
همچنین برای اعمال Inline Editing به عناصر لیست میتوانید روی دکمه ای با همین نام در Ribbon کلیک کنید
در ادامه میتوانید عملیات مرتب سازی و گروه بندی اطلاعات را هم تنها با چند کلیک مدیریت کنید
در اینجا گروه بندی و مرتب سازی را روی نام شرکت اعمال میکنیم :
تا کنون خروجی پیش نمایش داده شده در SPD به این صورت است :
تغییرات را ذخیره میکنیم :
و خروجی را در مرور گر مشاهده میکنید (همانطور که مشاهده میکنید ، ویرایش خطی برای آیتمها فعال شده است و همین طور گروه بندی بر اساس نام شرکت و مرتب سازی نیز اعمال شده است . همچنین رنگ سطر هایی که نام شرکت آنها Alfa است ، تغییر کرده است ) :
اکنون میخواهم قسمتی از تغییرات اعمال شده را از طریق مرور گر ( و نه SPD) تغییر دهم . برای این کار روی List در Ribbon کلیک میکنیم :
در قسمت Group by تنظیمات ستون را روی none قرار میدهیم تا گروه بندی اعمال نشود :
ASP.NET MVC #11
در صورت امکان فایل مستندات کدها قرار داده شود
Install-Package AngularTranslate
var app = angular.module('at', ['pascalprecht.translate']); app.config(function ($translateProvider) { $translateProvider.translations('en', { TITLE: 'Hello', FOO: 'This is a paragraph.', BUTTON_LANG_EN: 'english', BUTTON_LANG_DE: 'german' }); $translateProvider.translations('de', { TITLE: 'Hallo', FOO: 'Dies ist ein Paragraph.', BUTTON_LANG_EN: 'englisch', BUTTON_LANG_DE: 'deutsch' }); $translateProvider.preferredLanguage('en'); }); app.controller('Ctrl', function ($scope, $translate) { $scope.changeLanguage = function (key) { $translate.use(key); }; });
قبل از شروع به طراحی UI باید کمی با واحدهای اندازه گیری در اندروید آشنا شویم. بدانید و آگاه باشید که استفاده از واحد Pixel برای تعیین اندازه در اندروید کار بسیار اشتباهی است. طراح همیشه باید Density یا تراکم صفحهی نمایش را در نظر بگیرد. تراکم صفحهی نمایش به معنای تعداد پیکسل موجود در یک اینچ میباشد. اندازهی 100 پیکسل در دستگاههای مختلف با (dpi(Dot Per Inchهای متفاوت به یک اندازه نیست.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:id="@+id/MyButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/Hello" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:background="#fff" android:id="@+id/NameListView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); List<string> namesList = new List<string> { "Mohammad","Fatemeh","Ali","Hasan","Husein","Mohsen","Mahdi", }; var namesAdapter = new ArrayAdapter<string> (this, Android.Resource.Layout.SimpleListItem1, namesList); var listview = FindViewById<ListView>(Resource.Id.NameListView); listview.Adapter = namesAdapter; }
ListView کنترل بسیار منعطفی میباشد. برخی ویژگیها آن را در زیر میتوانید مشاهده بفرمایید:
- android:dividerHeight // ارتفاع جداکنندهی سطرها
- android:divider // رنگ جداکنندهی سطرها
- android:layoutAnimation // انیمیشن برای layoutها
- android:background // رنگ ضمینه را مشخص میکند. البته میتوانید یک style را به ان نسبت دهید
خوب؛ حالا بیایید یک ListView را با ظاهر و Adapter سفارشی بسازیم.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="14dp"> <TextView android:text="" android:gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/idTextView" /> <TextView android:text="" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/nameTextView" android:layout_marginLeft="14dp" /> </LinearLayout>
namespace DotSystem.ir.App1.Model { public class Person { public int Id { get; set; } public string PersonName { get; set; } }
namespace DotSystem.ir.App1.Adapters { public class PersonAdapter : BaseAdapter<Model.Person> { public override Person this[int position] { get { throw new NotImplementedException(); } } public override int Count { get { throw new NotImplementedException(); } } public override long GetItemId(int position) { throw new NotImplementedException(); } public override View GetView(int position, View convertView, ViewGroup parent) { throw new NotImplementedException(); } } }
در اینجا ما به چند فیلد داخل کلاس احتیاج داریم.
- لیست اطلاعات مورد نظر.
- Activity جاری که Adapter را استفاده میکند.
namespace DotSystem.ir.App1.Adapters { public class PersonAdapter : BaseAdapter<Person> { protected Activity _activity = null; protected List<Person> _list = null; public PersonAdapter(Activity activity, List<Person> list) { _activity = activity; _list = list; } public override Person this[int position] { get { return _list[position]; } } public override int Count { get { return _list.Count; } } public override long GetItemId(int position) { return _list[position].Id; } public override View GetView(int position, View convertView, ViewGroup parent) { throw new NotImplementedException(); } } }
public override View GetView(int position, View convertView, ViewGroup parent) { if (convertView == null) convertView = _activity.LayoutInflater .Inflate(Resource.Layout.PersonListViewItemLayout, parent, false); var idTextView = convertView.FindViewById<TextView>(Resource.Id.idTextView); var nameTextView = convertView.FindViewById<TextView>(Resource.Id.NameListView); var persion = _list[position]; idTextView.Text = persion.Id.ToString(); nameTextView.Text = persion.PersonName; return convertView; }
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); List<Model.Person> personList = new List<Model.Person> { new Model.Person() {Id = 1, PersonName = "Mohammad", }, new Model.Person() {Id = 2, PersonName = "Ali", }, new Model.Person() {Id = 3, PersonName = "Fatemeh", }, new Model.Person() {Id = 4, PersonName = "hasan", }, new Model.Person() {Id = 5, PersonName = "Husein", }, new Model.Person() {Id = 6, PersonName = "Mohsen", }, new Model.Person() {Id = 14, PersonName = "Mahdi", }, }; var personAdapter = new Adapters.PersonAdapter(this, personList); var listview = FindViewById<ListView>(Resource.Id.NameListView); listview.Adapter = personAdapter; }
{ title: 'Code', func: 'showCodesPage' },
پیاده سازی UnitOfWork به وسیله MEF
من الان یک ویژگی StringLength به طول 30 رو روی خاصیت Title کلاس Category مقاله جاری اضافه کردم. با همین کدهای فوق، این فیلد با طول 30 الان در دیتابیس قابل مشاهده است. آغاز دیتابیس اصلا کاری به MEF نداره.
این هم فایل مثالی که در آن ویژگی طول رشته اعمال شده برای آزمایش:
ASP.NET MVC #12
- برای حالت nullable هم همین فایل کافی است. یعنی در اصل بهتر است DisplayTemplate برای حالت nullable نوشته شود که برای هر دو حالت مورد استفاده قرار خواهد گرفت.
- در مورد validator هم میتونید یک attribute سفارشی تهیه کنید. در قسمت 13 راه کلی انجام کار رو توضیح دادم. برای تاریخ شمسی بحث مفصلی است. یک کلاس قبلا در این مورد تهیه کردم : (^) البته این فقط یک ایده برای شروع است که چه فرمتهایی میتونه وارد بشه و قابل قبول باشه.