2012/05/31

‫تجاربی از CI و Automatic Unit Testing با TeamCity


به سلامتی TeamCity را به قصد راه اندازی CI و انجام خودکار Unit Testها راه انداختیم اما مشکلات و مسائل زیادی به وجود آمد. بعضی‌ها حل شدند و بعضی‌ها نه. خلاصه‌ای از نکاتی که به آن برخورد کردم:

۱- نصب خود TeamCity خیلی ساده و سریع بود. ولی تا آنجا که فهمیدم فقط با MSBuild می‌شود با آن کار کرد و مثل TFS نیست که بشود با WorkFlow آن کارهای اضافی کرد.

۲- با آن که از git استفاده می‌کنیم ولی خوشبختانه مسائل ارتباط با git خیلی خیلی کمتر از آنی بود که فکر می‌کردم. حتی بخش CI که بایستی با push کردن هر تغییر تریگر شود بدون کمترین مشکلی کار کرد (اقلاً مشکل خاصی حس نکردم).

۳- معرفی Build Agentها به دلایلی مثل روشن بودن فایروال، نبودن git در مسیر اجرایی ویندوز و غیره اذیت کرد ولی نه خیلی.

۴- مشکل NuGet فوق‌العاده شدید بود. به راحتی آب خوردن یکی دو روز از وقتم را گرفت. با آن که تنظیم کرده بودم که دریافت packageها خودکار باشد ولی TeamCity با آن که packageها را دریافت می‌کرد باز هم build error می‌داد. به عنوان یک راه حل موقتی فایل‌های packageها را دستی در Build Agentها کپی کردم. ولی بعد از کش و قوس‌های فراوان تصمیم گرفتم NuGet را کنار بگذارم و همه Assemblyها مورد نیاز در داخل سورس کنترل اضافه کنم.

۵- مشکل بعدی مربوط به build target در یکی از Agentها بود. اول فکر کردم مشکل به خاطر نصب نبودن MVC 3 (پروژه مورد استفاده ما MVC 3 است) در agent است. اما متوجه شدم آنجام MVC 3 نصب هست و احتمالا مشکل از نصب نبودن Visual Studio 2010 در آنجا است. به هر صورت تصمیم گرفتم کمی کثیف کاری کنم (راه بهتری پیدا نکردم) و فایل ‎.targets مربوطه به agent مشکل دار کپی کنم.

۶- قدم بعدی انجام اتوماتیک Unit Testها بود. تنظیم آن محدود به یک Build Step ساده بود ولی در اولین قدم معرفی اسمبلی حاوی test کمی مشکل ایجاد کرد که با کمی سعی و خطا حل شد. اگر ساختار فولدر پروژه عوض شود باید آن را هم به روز رسانی کنم.

۷- مشکل Unit Test محدود به مشکل قبلی نبود. در یکی Agentها که ۶۴ بیتی بود، Unit Testها Run نمی‌شدند. در Unit Testهای مورد اشاره، عملیات دیتابیسی با استفاده دیتابیس داخل حافظه‌ای SQLite تست می‌شد. یادم آمد که SQLite دو نسخه مجزا برای محیط‌های ۳۲ بیتی و ۶۴ بیتی دارد. باید راهی پیدا می‌کردم تا Testها هم در ماشین ۳۲ بیتی اجرا شوند و هم در ماشین ۶۴ بیتی. تازه ماشین‌های development هم همگی ۳۲ بیت بودند و نباید یکپارچگی را از دست می‌دادم. مناسب‌ترین راه حل این بود که در زمان اجرا (Run Time) تشخیص می‌دادم که محیط ۳۲ یا ۶۴ بیتی است و بعد اسمبلی متناظر را به application اضافه می‌کردم. این راه زیادی Low Level بود. یک راه دیگر استفاده از GAC بود. اما من نهایتاً یک راه ساده و کثیف را (اقلا موقتی) انتخاب کردم. نسخه ۶۴ بیتی SQLite را دستی در فولدر Work (مسیر خاص پروژه من) کپی کردم و مشکل حل شد. حالا کل سیستم ۳۲ بیت بود ولی آن Agent خاص برای انجام Unit Testها از نسخه ۶۴ بیت خودش استفاده می‌کرد.




‫‫‫Database Unit Testing در Sharp Arch.‎



یکی از روش‌های رایج در Unit Test کدهای مربوط به دیتابیس این است که یک دیتابیس داخل حافظه‌ای ساخته شده و همه تست‌ها روی آن انجام شود. در پروژه‌های NHibernate این کار معمولا طی مراحل زیر انجام می‌شود:

۱- انجام تنظیمات دیتابیس Sqlite به صورت داخل حافظه‌ای
۲- ایجاد خودکار دیتابیس از روی mappingها در مرحله Setup تست واحد
۳- ایجاد/تزریق داده‌های امتحانی مورد نیاز در ادامه مرحله قبل
۴- انجام unit test مورد نظر

در پروژه‌های قبلی، مرحله ایجاد دیتابیس و تنظیمات session را به طور کاملا دستی انجام می‌دادیم اما از وقتی که از Sharp Arch. استفاده می‌کنیم می‌توان از روش‌های ساده‌تری استفاده کرد. مراحل انجام کار:

۱- ایجاد ارجاع به SharpArch.Tests
۲- ارث بری همه testها از RepositoryTestsBase
۳- initialization اولیه Service Locator در Setup تست
۴- انجام تست‌های مورد نظر

یک نمونه تست را در ادامه می‌بینیم:

using ProjectName.Domain;
using ProjectName.Domain.Contracts.Tasks;
using ProjectName.Infrastructure.Repositories;
using ProjectName.Tasks;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using CommonServiceLocator.WindsorAdapter;
using Microsoft.Practices.ServiceLocation;
using NUnit.Framework;
using SharpArch.Domain.PersistenceSupport;
using SharpArch.NHibernate;
using SharpArch.Testing.NUnit.NHibernate;

namespace ProjectName.Tests
{
    [TestFixture]
    public class SimpleEntityTaskTest : RepositoryTestsBase
    {
        private readonly ISimpleEntityTask _simpleEntityTask;

        public SimpleEntityTaskTest(ISimpleEntityTask simpleEntityTask)
        {
            _simpleEntityTask = simpleEntityTask;
        }

        [SetUp]
        protected override void SetUp()
        {
            ServiceLocatorInitializer.Init();
            base.SetUp();
        }

        protected override void LoadTestData()
        {
            //load test data
        }

        [Test]
        public void FindAll()
        {
            Assert.AreEqual(0, _simpleEntityTask.FindAll().Count);
        }
    }
}




2012/05/02

‫Authentication با استفاده Windows

در پروژه‌های ASP.NET وقتی که نیاز به Authentication/Authorization (ورود و خروج به سایت و سطح دسترسی‌ها) هست معمولاً از Form Authentication استفاده می‌شود. در این روش یا از Provider توکار ASP.NET استفاده می‌شود یا یک Provider سفارشی مثل SqliteProvider ساخته می‌شود.

اما گاهی اوقات هست که هم عجله داریم و هم این که کاربران سایت در حد دو سه نفر بوده و اتفاقاً آن دو سه نفر هم در سیستم کاربری ویندوز سرور کاربر تعریف شده دارند. این طور وقت‌ها می‌توان از یک راه خیلی سریع رفت. در این راه خیلی سریع نیاز به اضافه کردن صفحات login به سیستم و تعریف دسترسی‌ها در Controllerها یا web.configهای فرعی وجود ندارد. فقط فایل web.config است که تغییراتی می‌کند.

تغییرات web.config عبارتند از:

۱- استفاده از حالت Windows در authentication
۲- اضافه کردن نود کامل location به طوری که مسیرها و نام کاربری‌های دلخواه در آن تعریف شده باشد.
۳- اضافه کردن locationهای دیگر برای کنترل دسترسی به urlها مختلف سایت.