ساختار تیم‌های مدرن نرم‌افزاری قسمت ششم: ارتباط با مشتریان

ساختار تیم‌های مدرن نرم‌افزاری

پیشتر در یک سری نوشته هفت قسمتی درباره مهارت‌های کار تیمی نرم‌افزار صحبت کردیم. در سری نوشته ساختار تیم‌های مدرن نرم‌افزاری یک گام جلوتر رفته و به جای مقدمات کار تیمی، درباره اصول کار تیمی در دنیای مدرن تولید نرم‌افزار صحبت می‌کنیم. سایر نوشته‌های این سری را در این صفحه می‌توانید ببینید. 

این آخرین نوشته در سری نوشته ساختار تیم‌های مدرن نرم‌افزاری است.


نرم‌افزار دیگر یک کالای لوکس نیست
با یک تاخیر فاز نسبت به تحولات جهانی، حالا در ایران هم نرم‌افزار به بخش جداناپذیر از کسب‌وکارها تبدیل شده، همه از مغازه‌ها تا شرکت‌های بزرگ و سازمان‌ها درگیر انواع نرم‌افزارهای دسکتاپ، وب، موبایل و شبکه‌های اجتماعی هستند.
کسب و کارهای سنتی به سرعت بازار خود را به رقبایی که از نرم‌افزار استفاده بهینه می‌کنند واگذار می‌کنند (رواج تاکسی‌های اینترنتی و کساد شدن بازار تاکسی تلفنی‌های سنتی را به عنوان نمونه مشاهده کنید). در این شرایط وقتی نرم‌افزار هر روز بیشتر از قبل در سبد کالاهای مورد احتیاج مردم قرار می‌گیرد، تیم‌های تولید نرم‌افزار ارتباط گسترده‌تری با مشتریان نهایی پیدا می‌کنند.
به طور کلی چه نرم‌افزارهای سفارش مشتری و چه نرم‌افزارها و وب‌سایت‌ها و اپلیکیشن‌های عمومی، با بازه‌ای از مشتریان برخورد می‌کنند که از تولیدکنندگان نرم‌افزار انتظار ارائه خدمات بهتر یا نسخه‌های جدیدتر و امکانات بیشتر دارند. اما چطور می‌توان ارتباط بهتری برقرار کرد؟

شنیدن و انتخاب کردن
تیم‌های مدرن نرم‌افزاری، حرف مشتریان را می‌شنوند. پیشتر درباره اینکه چطور به ساز همه نرقصیم در مطلب استفاده از فیدبک کاربران، صحبت کردیم. واقعیت این است که اگر محصول نرم‌افزاری شما به اندازه کافی با نیاز بازار متناسب نباشد، مثل هر کالای دیگر فروش نخواهد رفت. انحصار در ارائه خدمات نرم‌افزاری در طلایی‌ترین دورانش (تلاش‌های انحصارطلبانه مایکروسافت) هم نتوانسته است بازار را راضی کند. اکنون در دوره‌ای هستیم که چند جوان علاقمند و پرتلاش می‌توانند در مدت نسبتاً کوتاهی، سرویس نرم‌افزاری بین‌المللی ارائه کنند.
در این شرایط اگر درب دریافت پیشنهادات و انتقادات تیم شما به روی مشتریان بسته است، به زودی منتظر پایان تلخ کار خود باشید!
نمودار زیر ۲۰ دلیل اصلی شکست استارتاپ‌ها را بررسی می‌کند که در صدر آن «نادیده گرفتن مشتریان» است. گاهی اوقات، نیت «گوش کردن به مشتری» در تیم‌ها هست اما ابزار و بستر آن وجود ندارد. مطمئن شوید که راه شفافی برای ارتباط با مشتریان خود دارید و به آن‌ها گوش کنید.

امکانات موثر به جای امکانات زیاد
هنوز در دیدگاه برخی مشتریان، نرم‌افزارها با کمیت امکاناتشان مقایسه می‌شوند. درباره معیارهای انتخاب درست نرم‌افزار توسط مشتریان باید در سری نوشته دیگری صحبت کرد، اما اینجا لازم است اشاره کنم آنچه برای مشتریان ارزش‌آفرینی می‌کند، تعدد امکانات یک نرم‌افزار (که ممکن است از بعضی از آن امکانات هیچ وقت استفاده نشود) نیست، بلکه موثر بودن امکانات نرم‌افزار است.
در دریافت نظرات مشتریان برای توسعه نرم‌افزارها و خدمات نرم‌افزاری، آن‌هایی را که مشکلات معناداری را حل می‌کنند انتخاب کنید. پس از ارائه امکانات، خروجی کار را بسنجید و مطمئن شوید به همان اندازه که فکر می‌کردید برای مشتری موثر بوده است. اینطوری هم مشتری از پولی که بابت قیمت خدمات شما پرداخته است راضی است و هم شما، وقت و انرژی تیم را صرف توسعه امکانات به دردنخور نکرده‌اید.

درد مشتری، درد من است
پیش از ادامه، لازم است روشن کنم وقتی از ارتباط مشتری با یک تیم مدرن نرم‌افزاری صحبت می‌کنیم، منظور این نیست که برنامه‌نویسان تیم مدام در حال پاسخگویی به تلفن مشتریان باشند!
در واقع بدترین کاری که می‌توانید انجام دهید این است که ارتباط مستقیمی بین درخواست مشتری و برنامه‌نویسان برقرار کنید. حداقل یک نفر/تیم دیگر این وسط لازم است تا درخواست‌ها و ارتباطات را پایش کرده و همانطور که در بالا اشاره شد، حرف مشتری را بشنود و امکانات موثر برای مشتری را انتخاب کند.
اما با وجود ارتباط غیرمستقیم، باید «درد مشتری» به تیم منتقل شود. اگر مشتری برای دریافت قابلیتی در حالت اضطرار قرار گرفته (نباید با عجله عمومی مشتریان اشتباه گرفته شود) باید این اضطرار در اجرا به تیم هم منعکس شود. تیم نرم‌افزاری باید از تاثیر خروجی کارش بر کسب و کار مشتری آگاهی داشته باشد.

راه حل جدید برای مشکلات قدیمی
به این سناریو توجه کنید: پروژه‌ای را به اتمام رسانده‌اید، کار تحویل شده و پول هم توسط مشتری پرداخت شده. مدتی بعد یک روش جدید برای اجرای همان پروژه پیدا می‌کنید یا از نظر فنی، راه‌های جدیدی برای رفع مشکلات مشتری پیدا می‌شود. چه می‌کنید؟
همواره به فکر به روزرسانی نرم‌افزاری مشتریان باشید. فناوری‌های تولید نرم‌افزار هر روز بهتر می‌شوند. فناوری بهتر در تولید، باعث افزایش سرعت تولید و حتی افزایش کیفیت تولیدات نرم‌افزاری می‌شود. از این فرصت استفاده کنید و به مشتری پیشنهاد ارتقاء بدهید. 

ارتباط داده محور به جای حدس و گمان
اگر مسئول سرویس نرم‌افزاری هستید که با مشتریان زیادی در ارتباط است، چطور کارآمد بودن سرویس خود را اندازه می‌گیرید؟ شاید بگویید با «شنیدن از مشتریان» 
واقعیت این است که بعضی حرف‌های مشتریان، از رفتار آن‌ها شنیده می‌شود. برای شنیدن این حرف‌های رفتاری، باید رفتار مشتریان را لاگ و آنالیز کنید.
در این حالت ارتباط شما با مشتریان به جای آن‌که بر اساس حدس و گمان باشد، بر پایه داده‌های حاصل از رفتار آن‌ها در نرم‌افزار است. کجا کلیک می‌کنند؟ چه صفحاتی را بیشتر می‌بینند؟ کجای نرم‌افزار کم باز می‌شود؟ و سوالاتی از این دست به شما کمک می‌کند تا ببینید UI‌ و UX نرم‌افزار و ارتباط شما با مشتری در چه وضعیتی است.

امیدوارم مجموعه مباحث مطرح شده در خصوص ساختار تیم‌های مدرن نرم‌افزاری مورد توجه شما قرار گرفته باشد. مثل همیشه پذیرای نظرات خوب شما هستیم.

به اشتراک گذاری این نوشته در شبکه‌های اجتماعی

۳۰ روز با TDD: روز هجدهم - بازبینی Refactoring قسمت اول

داستان ۳۰ روز با TDD

سپتامبر سال ۲۰۱۳ آقای James Bender در وبلاگ‌های تلریک یک مجموعه نوشته منتشر کرد به نام ۳۰ روز با TDD. در ترجمه‌ای آزاد در وبلاگ آرایه، با هم در طول یک ماه با Test Driven Development آشنا می‌شویم. لینک سایر نوشته‌های این سری را در صفحه ۳۰ روز با TDD می‌توانید مشاهده کنید.

و اما هجدهمین روز: بازبینی Refactoring

توجه: قبل از این نوشته، آزمون‌های واحد (Unit testها) مربوط به تغییرات PlaceOrder نوشته قبلی را از اینجا می‌توانید دانلود کنید.

در چند نوشته گذشته، متد PlaceOrder را از OrderService بیرون بردیم. برای مرور، متد فعلی این شکلی است:


public Guid PlaceOrder(Guid customerId, ShoppingCart shoppingCart)
        {
            foreach (var item in shoppingCart.Items)
            {
                if (item.Quantity == 0)
                {
                    throw new InvalidOrderException();
                }
            }

            var customer = _customerService.GetCustomer(customerId);

            //Open Session
            var orderFulfillmentSessionId = _orderFulfillmentService.OpenSession(USERNAME, PASSWORD);
            
            var firstItemId = shoppingCart.Items[0].ItemId;
            var firstItemQuantity = shoppingCart.Items[0].Quantity;

            //Check Inventory Level
            var itemIsInInventory = _orderFulfillmentService.IsInInventory(orderFulfillmentSessionId, firstItemId, firstItemQuantity);

            //Place Order
            var orderForFulfillmentService = new Dictionary();
            orderForFulfillmentService.Add(firstItemId, firstItemQuantity);
            var orderPlaced = _orderFulfillmentService.PlaceOrder(orderFulfillmentSessionId, 
                orderForFulfillmentService, 
                customer.ShippingAddress.ToString());
                       
            //Close Session
            _orderFulfillmentService.CloseSession(orderFulfillmentSessionId);

            var order = new Order();
            return _orderDataService.Save(order);
        }

این متد کمی طولانی شده و همچنین داریم به محدوده نقض Single Responsibility Principel (برای مرور SRP روز پنجم را مطالعه کنید) وارد می‌شویم. در حال حاضر شش دلیل برای اینکه این متد باید تغییر کند شمردم:

۱. اعتبارسنجی هر آیتم سبد خرید
۲. روش باز شدن session ها با سرویس order fulfillment
۳. روش چک کردن انبار با سرویس order fulfillment
۴. روش ثبت سفارش با سرویس order fulfillment
۵. بسته شدن session ها با order fulfillment
۶. ایجاد و ذخیره یک سفارش

در لیست بالا «ایجاد و ذخیره یک سفارش» موردی است که کمترین نگرانی را درباره‌اش دارم. کد زیادی درباره ایجاد و ذخیره سفارش نیست پس فعلاً جای نگرای نیست. این موضوع بعداً در جریان توسعه نرم‌افزار قطعاً تغییر می‌کند اما فعلاً در رادار من نیست. مورد اول هم گرچه جای نگرانی دارد ولی در حال حاضر بدترین مشکل نقض SRP نیست، بنابراین می‌تواند منتظر بماند.

چیزی که الان می‌خواهم رویش تمرکز کنم موارد ۲ تا ۵ هستند. همه این موارد با سرویس order fulfillment در ارتباطند و بخش زیادی از کد را تشکیل می‌دهند.

توسعه‌دهنده‌هایی که درباره ایده SRP یا refactoring تازه‌وارد هستند احتمالاً به لیست بالا نگاه می‌کنند و فکر می‌کنند احتمالاً به ۴ متد جدید برای هر یک از مسائل ۲ تا ۵ نیاز داریم. این اشتباه نیست، اما بخشی از داستان است. اینکه خیلی ساده ۴ متد جدید اضافه کنیم، کد را خواناتر و قابل استفاده مجددتر می‌کند، اما همچنین شش دلیل برای تغییر PlaceOrder خواهیم داشت. SRP‌ و refactoring فقط این نیست که کدها را به متدهای خصوصی (private methods) ببریم تا متدهای اصلی را کوتاه‌تر کنیم، بلکه درباره ایجاد تجرد منطقی (logical abstraction) در کد است تا مرا در برابر تغییرات آینده محافظت کند.

آنچه می‌بینم، حجم زیادی پروسه کار با سرویس order fulfillment است که می‌خواهم از متد PlaceOrder جدایش کنم. وقتی این کار انجام شد، برای کل این پروسه طولانی کار با order fulfillment فقط یک متد باید فراخوانی کنم. این کار مرا در برابر تغییرات مرتبط در هر یک از مراحل کار با سرویس order fulfillment محافظت می‌کند. پس قدم اول استخراج همه کدهایی که با سرویس order fulfillment کار می‌کنند برای ایجاد یک متد خصوصی جداگانه است. چون از Just Code (محصول شرکت تلریک) استفاده می‌کنم این کار ساده است: می‌توانم یک بلوک کد را انتخاب و از منوی کمکی Just Code گزینه Extract Method را مشابه تصویر زیر انتخاب کنم یا از کلید میانبر (CTRL + R, CTRL + M) استفاده کنم و Just Code بخش انتخاب شده را برای یک متد خصوصی جدید استخراج می‌کند.

Just Code کد مورد نیاز را به همراه پارامترهای مورد نیاز استخراج می‌کند و از من درباره نام متد سوال می‌کند و من هم نام PlaceOrdeWithFulfillmentService را انتخاب می‌کنم. همچنین کد اصلی را با فراخوانی این متد تازه ساخته شده تغییر می‌دهد.

public Guid PlaceOrder(Guid customerId, ShoppingCart shoppingCart)
        {
            foreach (var item in shoppingCart.Items)
            {
                if (item.Quantity == 0)
                {
                    throw new InvalidOrderException();
                }
            }

            var customer = _customerService.GetCustomer(customerId);

            PlaceOrderWithFulfillmentService(shoppingCart, customer);

            var order = new Order();
            return _orderDataService.Save(order);
        }
  
        private void PlaceOrderWithFulfillmentService(ShoppingCart shoppingCart, Customer customer)
        {
            //Open Session
            var orderFulfillmentSessionId = _orderFulfillmentService.OpenSession(USERNAME, PASSWORD);
              
            var firstItemId = shoppingCart.Items[0].ItemId;
            var firstItemQuantity = shoppingCart.Items[0].Quantity;

            //Check Inventory Level
            var itemIsInInventory = _orderFulfillmentService.IsInInventory(orderFulfillmentSessionId, firstItemId, firstItemQuantity);

            //Place Orders
            var orderForFulfillmentService = new Dictionary();
            orderForFulfillmentService.Add(firstItemId, firstItemQuantity);
            var orderPlaced = _orderFulfillmentService.PlaceOrder(orderFulfillmentSessionId,
                orderForFulfillmentService,
                customer.ShippingAddress.ToString());
                         
            //Close Session
            _orderFulfillmentService.CloseSession(orderFulfillmentSessionId);
        }

تاثیر این کار بر روی متد PlaceOrder خیلی زیاد است و همین حالا هم بسیار خواناتر شده. متوجه شدم که JustCode متد تازه ساخته شده را با نوع بازگشتی void ایجاد کرده است. این به خاطر آن است که در هیچ کجای کد استخراج شده مقداری تولید نمی‌شود که در ادامه کد اصلی از آن استفاده شده باشد. در این مورد در نوشته بعدی بیشتر صحبت می‌کنیم. برای تایید این کارها و اینکه این تغییرات کد را دچار مشکل نکرده، باید unit test ها را اجرا کنم.

نتیجه اجرای تست‌ها به این معنی است که اولین refactor من کار کرده و نوع بازگشتی متد PlaceOrderWithFulfillmentService فعلاً می‌تواند void باشد. حالا اجازه بدهید به سراغ متد تازه ایجاد شده برویم و refactor کنیم.

متد PlaceOrderWithFulfillmentService باعث شده که کد ما کمتر شکننده باشد، اما خودش هم مشکلاتی دارد. ما به refactor بیشتری نیاز داریم و حالا می‌خواهم هر یک از مراحل را به متدهای خودشان منتقل کنم. به سراغ اولین مرحله می‌روم: OpenOrderFuilfillmentService


private void PlaceOrderWithFulfillmentService(ShoppingCart shoppingCart, Customer customer)
        {
            //Open Session
            var orderFulfillmentSessionId = OpenOrderFulfillmentSession();
              
            var firstItemId = shoppingCart.Items[0].ItemId;
            var firstItemQuantity = shoppingCart.Items[0].Quantity;

            //Check Inventory Level
            var itemIsInInventory = _orderFulfillmentService.IsInInventory(orderFulfillmentSessionId, firstItemId, firstItemQuantity);

            //Place Orders
            var orderForFulfillmentService = new Dictionary();
            orderForFulfillmentService.Add(firstItemId, firstItemQuantity);
            var orderPlaced = _orderFulfillmentService.PlaceOrder(orderFulfillmentSessionId,
                orderForFulfillmentService,
                customer.ShippingAddress.ToString());
                         
            //Close Session
            _orderFulfillmentService.CloseSession(orderFulfillmentSessionId);
        }
  
        private Guid OpenOrderFulfillmentSession()
        {
            var orderFulfillmentSessionId = _orderFulfillmentService.OpenSession(USERNAME, PASSWORD);
            return orderFulfillmentSessionId;
        }

اجرای تست‌ها نشان می‌دهد که این refactor هم موفق بوده است. حالا مرحله بستن session را هم به متد جدیدی به نام CloseOrderFulfillmentService می‌برم

private void PlaceOrderWithFulfillmentService(ShoppingCart shoppingCart, Customer customer)
        {
            //Open Session
            var orderFulfillmentSessionId = OpenOrderFulfillmentSession();
              
            var firstItemId = shoppingCart.Items[0].ItemId;
            var firstItemQuantity = shoppingCart.Items[0].Quantity;

            //Check Inventory Level
            var itemIsInInventory = _orderFulfillmentService.IsInInventory(orderFulfillmentSessionId, firstItemId, firstItemQuantity);

            //Place Orders
            var orderForFulfillmentService = new Dictionary();
            orderForFulfillmentService.Add(firstItemId, firstItemQuantity);
            var orderPlaced = _orderFulfillmentService.PlaceOrder(orderFulfillmentSessionId,
                orderForFulfillmentService,
                customer.ShippingAddress.ToString());
                         
            //Close Session
            CloseOrderFulfillmentService(orderFulfillmentSessionId);
        }
  
        private void CloseOrderFulfillmentService(Guid orderFulfillmentSessionId)
        {
            //Close Session
            _orderFulfillmentService.CloseSession(orderFulfillmentSessionId);
        }

اجرای تست‌ها نشان می‌دهد که همچنان کد بدون مشکل کار می‌کند.

دو مرحله باقیمانده کمی سخت‌تر هستند، بنابراین در نوشته بعدی درباره‌اش صحبت می‌کنیم.

ادامه دارد...

به اشتراک گذاری این نوشته در شبکه‌های اجتماعی

نگاهی به سرویس ایرانی mbaas

شریک تجاری آرایه

مدتی است اپلکیشن رسمی آرایه برای اندروید در اختبار مشتریان محترم قرار گرفته است. در این اپلکیشن علاوه بر

اخبار و مطالب وبلاگ و لینک‌های روزانه آرایه، امکان پیگیری تیکت‌ها و مشاهده فاکتورها نیز پس از ورود با نام کاربری و کلمه عبور برای مشتریان در نظر گرفته شده است. 

این اپلکیشن حاصل همکاری تجاری توسعه نرم‌افزار آرایه و روناک اندیشان فردا بوده است. محصول روناک اندیشان فردا که به تولید این اپلیکیشن کمک شایانی کرده است، سرویس سایت mbaas.ir است که در این نوشته با هم بررسی می‌کنیم.

mbaas.ir چیست؟

آنطور که در سایت mbaas.ir آمده است، mbaas مجموعه‌ای از خدمات برای توسعه‌دهندگان برنامه‌های موبایل است که در قالب یک سرویس یکپارچه ارائه می‌شود. mbaas سرنام عبارت Mobile Backend as a Service است.

چه خدماتی در mbaas ارائه می‌شود؟

در حال حاضر ۴ خدمت اصلی در mbaas ارائه می‌شود:

  • Push Notification
  • Crash Reporter
  • Mobile Backend
  • API
علاوه بر این خدمات اصلی، خدمات دیگری مانند آمار نصب برنامه و امکان به روز رسانی خودکار و ... نیز عرضه می‌شود که در ادامه به آن‌ها اشاره خواهد شد.

Push Notification در mbaas چطور کار می‌کند؟

سرویس‌های مختلفی در مجموعه سرویس‌های mbaas از طریق SDK سایت mbaas عرضه می‌شود. توسعه دهنده موبایل این SDK را به برنامه خود می‌افزاید و می‌تواند از خدماتی مانند Push Notification و یا ACRA استفاده کند.

این SDK اپن سورس است. یکی از مزایای mbaas در این حوزه وجود مستندات خوب و کامل متنی برای ارسال Push Notification است

منظور از زیرساخت موبایل در mbaas چیست؟

برنامه‌های موبایلی که به ارائه خدمات به کاربران می‌پردازند، مانند برنامه آرایه از مجموعه‌ای از فراخوانی وب‌سرویس‌ها و ارتباطات احراز هویت شده تشکیل شده‌اند. 

زیر ساخت موبایل در mbaas، زیرساختی را در اختیار توسعه‌دهندگان می‌گذارد تا به راحتی کاربران و فایل‌های ارسالی آنان و همچنین فراخوانی Rest API های دیگران یا ارتباط با بانک‌های اطلاعاتی و ... را مدیریت کنند. مزیت اصلی استفاده از این backend به جای backend سفارشی در سهولت و سرعت و همچنین امنیت ارائه برنامه است.

با استفاده از این زیرساخت، همچنین امکان توسعه برای انواع پلتفرم‌های موبایل از جمله android , iOS , windows وجود دارد. زیرساخت موبایلی mbaas همچنین امکان یکپارچگی با برنامه‌های تحت وب دات نت را نیز دارد.

تیم فنی mbaas علاوه بر در اختیار گذاشتن زیرساخت موبایل، API‌هایی نیز مانند پیش‌بینی وضعیت آب و هوا و نرخ طلا و ارز در اختیار توسعه‌دهندگان قرار داده که می‌توانند از این API‌ها در برنامه خود استفاده کنند.

مشاهده خطاهای برنامه با mbaas چگونه امکان پذیر است؟

هر برنامه اندرویدی ممکن است دچار خطا شود، با توجه به تعدد گوشی‌ها و نسخه‌های اندروید و دستکاری سازندگان تلفن همراه و همچنین تنوع سخت‌افزار و امکانات، تشخیص و رهگیری و رفع این خطاها به یک کابوس تبدیل می‌شود.

اینجاست که mbaas با ارائه سرویس ACRA به کمک برنامه‌نویسان موبایل می‌آید. این سرویس پس از نصب یک SDK رایگان در برنامه، خطاهای اتفاق افتاده را به همراه جزئیات کامل دستگاهی که crash روی آن اتفاق افتاده است را برای سرور mbaas ارسال می‌کند.

در پنل توسعه‌دهندگان در سرویس mbaas علاوه بر مشاهده خطاها، جزئیات آماری از آن خطاها نیز در قالب نمودار نمایش داده می‌شود

باز هم طبق روال، یک مستند فنی خوب درباره نحوه استفاده از این سرویس در برنامه‌ها نوشته شده است.

چه آمار و اطلاعاتی درباره برنامه‌هایی که از mbaas استفاده می‌کنند قابل مشاهده است؟

یکی از ویژگی‌هایی که اخیراً به mbaas اضافه شده است، امکان مشاهده آمار مربوط به نصب و استفاده از برنامه است. در این آمار علاوه بر تاریخ نصب، آخرین تاریخ استفاده و همچنین تعداد دفعاتی که کاربر از آن برنامه استفاده کرده و وضعیت نصب گوگل پلی قابل مشاهده است.

مزیت اصلی mbaas نسبت به رقبای داخلی دیگر چیست؟

پاسخ کوتاه: یکپارچگی

واقعیت این است که با داغ شدن تب برنامه‌های موبایل، سرویس‌های مختلفی در ایران برای ارائه خدمات به توسعه‌دهندگان موبایل در ایران ایجاد شده‌اند. mbaas هم یکی از این سرویس‌هاست. اما با توجه به همکاری تجاری بین آرایه و mbaas دو مزیت اصلی در شیوه کاری این سرویس خودنمایی می‌کند:

نخست یکپارچگی بی‌نظیر خدمات سایت است، از همان پنلی که خطاهای برنامه خود را می‌بینید می‌توانید push ارسال کنید یا نسخه جدید برنامه خود را به صورت اتوماتیک برای کاربرانتان ارسال کنید و ...

دوم به روزرسانی پیوسته سرویس mbaas است. mbaas در هر ماه با ویژگی‌های خوب و ریز و درشت مختلفی به روز می‌شود. این روند رو به رشد از زمان عرضه سرویس بی‌وقفه ادامه داشته و پویایی خدمات سایت در کنار پشتیبانی خوب (از طریق ایمیل یا انجمن و به زودی از طریق سرویس تیکت برای پلن‌های غیررایگان) mbaas را به انتخاب شماره یک توسعه‌دهندگان موبایل ایرانی در مقایسه با سایر رقبا تبدیل می‌کند.

اگر سرویس mbaas جمع شود چه بلایی سر برنامه‌های من می‌آید؟

یکی از دغدغه‌های مرتبط با سرویس‌های ایرانی، عمر کوتاه آن‌ها و سردرگمی کاربران پس از پایان ارائه سرویس است. mbaas با بیزنس پلن و خدمات خوبی که برای توسعه‌دهندگان موبایل دارد، عملاً تا مدت‌ها به خدمات رسانی خواهد پرداخت اما حتی در صورت خاتمه ارائه خدمات mbaas با توجه به رویکرد اپن سورسی که تیم mbaas در استفاده از خدمات و SDK محصولات دارد، توسعه‌دهنده‌ها می‌توانند مطمئن باشند که می‌توانند با کمترین وقفه، به استفاده از خدماتی نظیر ACRA و Push از سرویس‌دهنده‌های دیگر ادامه دهند.

قیمت استفاده از سرویس‌های mbaas چقدر است؟

پاسخ: نامشخص

شاید تنها نقطه ضعف فعلی mbaas شفاف نبودن سیاست مالی در قبال توسعه‌دهندگان است. در چند روز اخیر، در صفحه نخست سایت mbaas و در بخش تعرفه‌ها، محدودیت‌های پلن رایگان سایت معرفی شده و در عین حال دو پلن سبلان و دماوند بدون ذکر قیمت، معرفی شده‌اند. 

امیدواریم با مشخص شدن قیمت پلن‌ها، بتوان ارزیابی بهتری نسبت به ارزش‌های ارائه شده توسط mbaas به توسعه‌دهندگان موبایل داشت.

جمع بندی

با توجه به همه پرسش و پاسخ های بالا، سال ۹۵ برای توسعه‌دهندگان موبایل با حضور سرویس‌هایی مثل mbaas در بازار، سال بسیار خوبی خواهد بود. 

به اشتراک گذاری این نوشته در شبکه‌های اجتماعی

نگاهی به سرویس ایرانی اورست

درباره این گزارش

گزارش بررسی سرویس اینترنتی ایرانی «اورست» به درخواست شرکت ارائه‌دهنده سرویس نوشته شده است. تلاش آرایه این بوده است که سرویس را از جنبه‌های مختلف مورد بررسی قرار دهد.
این نوشته، گزارشی مشابه آنچه پیشتر در وبلاگ آرایه درباره Windows Azure خواندیم در قالب پرسش و پاسخ است و قطعاً با سوالات بیشتر شما می‌توان آن را به روز کرد.
لازم به توضیح است، این نوشته تنها قصد بررسی قابلیت‌های این سرویس را دارد و تصمیم و مسئولیت مرتبط با استفاده یا عدم استفاده از این سرویس متوجه آرایه نیست.

اورست چیست؟

آنطور که در سایت اورست آمده: «سرویس ابری(کلود) اورست، یک SaaS (نرم افزار به عنوان سرویس) امن و یکپارچه از نرم افزارهای شیرپوینت، پراجکت سرور، مایکروسافت CRM، اکسچنج و اسکایپ است.»

آیا اورست ابری است؟

پاسخ کوتاه: نه کاملاً
از آنجایی که سرویس‌دهندگان خدمات رایانش ابری (Cloud Computing) در ایران زیاد نیستند، اولین سوالی که مطرح می‌شود درباره ماهیت «ابری» سرویس است.
در نوشته‌ای که به بررسی سرویس ابری مایکروسافت (ویندوز آژور) می‌پرداخت توضیح دادیم که آژور سرویس‌هایش در ۱۹ دیتاسنتر در مناطق مختلف دنیا ارائه می‌کند، بر اساس اعلام ارائه‌دهنده سرویس «اورست» این سرویس بر روی بیش از یک سرور اما در یک دیتاسنتر ارائه می‌شود.
شرکت ارائه‌دهنده سرویس در پاسخ به سوال آرایه اعلام کرده‌اند که در صورت قطع شدن سرور اصلی، سرور دیگری بلافاصله به صورت اتوماتیک جایگزین می‌شود اما این سرور نیز در همان دیتاسنتر مخابرات قرار دارد، و لذا در صورت بروز مشکل در دیتاسنتر، سرویس از دسترس خارج خواهد شد.

علاوه بر مبحث دیتاسنتر، یکی دیگر از قابلیت‌های سرویس‌های عرضه شده در بستر رایانش ابری، امکان گسترش استفاده در هر زمان است. در حال حاضر توسعه پلان‌های سرویس اورست فقط در حوزه فضای ذخیره سازی و تعداد کاربر است و امکاناتی نظیر پردازش CPU یا استفاده از RAM سرور در اختیار کاربر نیست و از این جهت نیز اورست یک سرویس کاملاً ابری محسوب نمی‌شود.

چه اطلاعاتی را روی اورست می‌توان قرار داد؟

پاسخ کوتاه: همه چیز!
همانطور که در پاسخ به سوال اورست چیست اشاره شد، اورست مجموعه‌ای از نرم‌افزارها از جمله شیرپوینت و Project Server و CRM و Exchange و Skype را ارائه می‌کند.
این بدان معنی است که هرگونه اطلاعاتی را می‌توان روی این بستر قرار داد، اعم از اطلاعات مشتریان، شماره‌های تماس، چت‌های صوتی و تصویری، ایمیل‌ها و برنامه ریزی اجرای پروژه‌ها، اسناد در فرمت‌های مختلف و تصاویر و ...

آیا اورست امن است؟

پاسخ کوتاه: بلی
با توجه به پاسخ سوال قبل و اینکه همه اطلاعات یک سازمان یا شرکت را می‌توان در اورست قرار داد، مهمترین سوال امنیت اطلاعات است. اورست صفحه‌ای را به موضوع توضیح امنیت اطلاعات اختصاص داده است.
بخشی از راهکارها، مرتبط با تهیه نسخه پشتیبان از اطلاعات است و بعضی دیگر مربوط به امنیت سرور و جلوگیری از هک شدن آن. در صورتی که اجرای موارد مطرح شده توسط شرکت ارائه‌کننده در خصوص امنیت اطلاعات، سرویس اورست امنیت دارد.

آیا اطلاعات من در اورست توسط دیگران دیده می‌شود؟

پاسخ کوتاه: ممکن است دیگران اطلاعات شما را ببینند!
این سوال دو بخش دارد: اول شنود اطلاعاتی که از بستر امن منتقل نمی‌شوند و دوم مشاهده خود اطلاعات توسط افرادی که به سرورها دسترسی دارند که در پرسش بعدی آن را بررسی می‌کنیم.

در مورد سوال اول، استفاده از ssl و پروتکل https در سامانه‌ها فقط در CRM مشاهده شد و لذا ارتباطات با سایر نرم‌افزارهای اورست قابلیت شنود دارد. البته ما فقط نسخه دمو سرویس را مشاهده کردیم که در آن امکان مشاهده ایمیل و اسکایپ نبود. هر چند استفاده از خدمات ssl در لیست امکانات قابل درخواست در پلن‌های مختلف سرویس نیست، اما شرکت ارائه‌دهنده در پاسخ به سوال آرایه، اعلام کردند که در سرویس ایمیل و اسکایپ الزاما از ssl معتبر استفاده می‌کنند و برای شیرپوینت و Project Server نیز به اختیار مشتری امکان ارائه این خدمات را دارند.

آیا اطلاعات من رمز می‌شوند؟

پاسخ کوتاه: احتمالاً خیر
گرچه در صفحه امنیت اطلاعات در سایت اورست ادعا شده است که اطلاعات بر روی سرورها به صورت رمز شده ذخیره می‌شوند و فقط خود مشتری می‌تواند به آن‌ها دسترسی داشته باشد اما برای رمزنگاری، امکان تعریف یا آپلود کلید خصوصی در نظر گرفته نشده.

حال در صورتی که اطلاعات رمز شوند، به دلیل ذخیره کلیدها بر روی خود سرور، هم توسط مدیران سرور و کارمندان مجوزدار شرکت ارائه‌کننده، اطلاعات قابل مشاهده هستند و هم در صورت هک شدن سرور، اطلاعات رمز شده قابل رمزگشایی می‌باشند!

در سایت اورست در پاسخ به سوال «اگر این سایت جمع شد چه بلایی سر داده‌های من می‌آید؟» عنوان شده در صورت بسته شدن سرویس اورست، نسخه پشتیبان اطلاعات در اختیار گذاشته می‌شود، لذا بایستی به همراه نسخه پشتیبان، کلیدها نیز ارائه شوند.

در خصوص رمزنگاری در حال حاضر حتی سرویس Azure مایکروسافت نیز قابلیت استفاده از کلید رمزنگاری ارائه شده توسط مشتری را عرضه نکرده است (اعلام شده در حال کار بر روی این مورد هستند) و کلیدهای رمزنگاری توسط خود مایکروسافت ایجاد و مدیریت می‌شوند البته مشتریان می‌توانند کلیدهای خود را مشاهده و از آن نسخه پشتیبان تهیه کنند.

ارائه‌دهنده سرویس اورست در پاسخ به پرسش آرایه در خصوص توضیحات بیشتر درباره رمزشدن اطلاعات، اعلام کردند که اجازه توضیح درباره مسائل فنی را ندارند و به توضیح کلی که محصولات مایکروسافت از رمزنگاری SQL پشتیبانی می‌کنند بسنده کردند.

بر اساس مشاهدات توضیح داده شده، فکر می‌کنیم داده‌ها حتی در صورت رمزنگاری فقط برای خود شخص قابل بازیابی نیستند و شرکت ارائه‌دهنده نیز می‌تواند اطلاعات رمز شده را رمزگشایی کند. ذکر این نکته لازم است که در صورت درخواست مراجع قضایی می‌بایست امکان ارائه اطلاعات از سوی سرویس‌دهنده وجود داشته باشد.

مزیت اصلی اورست در مقایسه با نرم‌افزارهای محلی چیست؟

پاسخ کوتاه: یکپارچگی
محصولات مختلفی که در این سرویس عرضه می‌شوند، همه با هم یکپارچه هستند. شرکت ارائه کننده نیز سابقه چندین سال در ارائه راهکارهای حوزه مایکروسافت دارد و حتی ماژول‌های اختصاصی خودش را نیز برای یکپارچگی بهتر عرضه می‌کند. لذا اگر دنبال استفاده از یک راهکار یکپارچه تحت وب برای مدیریت پروژه، ارسال و دریافت ایمیل و مدیریت ارتباط با مشتریان و مدیریت اسناد و آفیس تحت وب و چت‌های سازمانی هستید، اورست مجموعه‌ای از نرم‌افزارهای سازمانی خوب مایکروسافت در این حوزه‌ها را به صورت یکپارچه عرضه می‌کند و در حال حاضر در ایران رقیبی برای این مجموعه خدمات مایکروسافتی نیست.

آیا می‌توان از سرورهای محلی به سرور اورست مهاجرت کرد؟

پاسخ کوتاه: بلی
یکی از مزایای استفاده از نرم‌افزارهای سازمانی مایکروسافت امکان انتقال سریع اطلاعات بین نسخه‌های محلی و سرویس‌هاست. همانطور که در صفحه توضیح مهاجرت به اورست اعلام شده است، با داشتن نسخه پشتیبان از بانک اطلاعاتی هر یک از نرم‌افزارها یا ارائه فایل اکسل اطلاعات به شرکت ارائه‌کننده امکان مهاجرت به این سرویس وجود دارد.

این یکی از مزایای خوب سرویس اورست است که مشتریان درگیر انتقال از نرم‌افزارهای قدیمی خود به سرویس جدید نمی‌شوند و در سریعترین زمان می‌توانند کسب و کار خود را در فضای سرویس اورست پی بگیرند.

اورست با چه قیمتی ارائه می‌شود؟

اورست در قالب ۸ پلان اصلی و یک پلان سفارشی عرضه می‌شود، همانطور که در پاسخ به سوال اول (ابری بودن اورست) اعلام شد به دلیل اینکه سرویس اورست یک سرویس ابری کامل نیست، نوع قیمت‌گذاری اورست نیز به صورت ابری نمی‌باشد.

قیمت‌ها از ماهیانه حدود ۹۶ هزار تومان آغاز و به ۳۸۰ هزار تومان در ماه ختم می‌شوند البته در پلن سفارشی می‌بایست برای قیمت سرویس که بر حسب نیازمندی مشتری تعیین می‌شود تماس بگیرید.

ارتقا سرویس در حوزه تعداد کاربر و فضای ذخیره سازی ممکن است و همانطور که پیشتر اشاره شد امکان تعیین میزان سهم از پردازش یا RAM نیست.

در سرویس‌های ابری بین‌المللی، قیمت هر یک از اجزای سرویس ابری مشخص است اما در اورست در پلن‌های ۸ گانه که به نام کوه‌های مختلف در ایران و کوه اورست نامگذاری شدند، شما مبلغ مشخصی را بابت استفاده از میزان مشخص منابع نرم‌افزاری می‌دهید (بدون اینکه بدانید کدام بخش قیمت مربوط به کدام نرم‌افزار است)

جمع بندی

اورست یک سرویس تازه نفس برای کسب و کارهاست. با توجه به اینکه در سال ۹۵ عملاً راه‌اندازی سرویس انجام شده، هنوز زود است در مورد آینده آن تصمیم بگیریم اما آینده ارائه سرویس‌های خدمات کسب و کار قطعاً درخشان است. ضمن آرزوی موفقیت برای تیم اورست، امیدواریم ارائه سرویس‌های اینچنینی در دستورکار سایر شرکت‌های نرم‌افزاری داخلی نیز قرار گیرد و البته بیش از پیش شاهد حضور نرم‌افزارهای داخلی در بستر رایانش ابری باشیم.

به اشتراک گذاری این نوشته در شبکه‌های اجتماعی

«پریدن تیک» یا وقتی یک «نرم افزار» برای کشور مشکل ایجاد می‌کند

 مدتی پیش نوشته‌ای در روزنامه خراسان تحت عنوان «ترخیص کالا قربانی سامانه بی سامان گمرک» توجهم را جلب کرد. بخشی از این نوشته را با هم بخوانیم:

پریدن تیک از سامانه هم یکی از مواردی است که بسیاری از ترخیص کاران و کارشناسان گمرک با آن دست به گریبان‌اند و به دلیل همین مشکل ممکن است چندین روز بین اتاق‌های گمرک و دستگاه‌هایی مانند وزارت راه، استاندارد یا غذا و دارو در آمدوشد باشند....
باز هم سامانه کار دست صاحب کالا داده است. این جا صاحب کالا دو گزینه دارد یا باید ضرر چهار میلیونی را بابت یک روز معطلی ۸ تریلر به جان بخرد یا ضررش را بر روی قیمت تمام شده اجناسش بکشد. علت را که می‌پرسم این جواب را می‌شنوم که تیک وزارت راه پریده است! بیشتر شبیه یک شوخی است. مگر می‌شود سامانه جامعی آن هم از آن مرزبان اقتصادی کشور چنین مشکل پیش پا افتاده‌ای داشته باشد و مجوزی که صادر شده پس از یک روز از بین برود؟!

نویسنده این گزارش، احتمالاً خبر ندارد که در نرم‌افزار همه چیز شدنی است! حتی پریدن تیک نرم‌افزار. در جایی از این گزارش اشاره می‌شود که هر روز معطلی کالا در گمرک معادل یک درصد ارزش کالا است. تصور کنید وجود یک نرم‌افزار کارآمد چقدر می‌تواند در تسهیل ورود و خروج کالا در گمرک اثرگذار باشد و برای اقتصاد داخلی سودمند باشد. در عین حال یک نرم‌افزار باگ دار می‌تواند ضررهای بزرگی را متوجه صاحبان کسب و کارها و مردم کند.

مشکل کجاست؟

دولت بزرگترین مشتری نرم‌افزار است. به دلیل گستردگی و تنوع فعالیت‌های دولتی، سازمان‌ها و بخش‌های مختلف دولت همواره نیازمند نرم‌افزار برای سرعت بخشیدن به کارها و افزایش دقت و شفافیت هستند. اما چه اتفاقی می‌افتد که نرم‌افزارهای تولید شده در داخل کشور، بعضاً با مشکلات متعددشان کارها را پیچیده‌تر می‌کنند؟ به گمان من چند عامل در این مساله نقش دارند که در نوشته‌های دیگری به آن‌ها خواهم پرداخت:

  • طولانی بودن پروسه تصمیم‌گیری درباره نیازهای نرم‌افزاری: این یک داستان تکراری است. نرم‌افزاری سفارش داده می‌شود اما تا روال اداری عقد قرارداد و جلسات تحلیل طی شود و نرم‌افزار نوشته شود یا بر اساس یک محصول نرم‌افزاری در سازمان مستقر شود، نیازمندی‌ها و قوانین و مقررات تغییر می‌کنند!
  • ضعف تحلیل نیازمندی‌ها: دنیای تجارت با دنیایی که در مثال‌های دانشگاهی ما بررسی می‌شود متفاوت است. نمونه‌هایی سراغ دارم از تحلیل‌های ناقص که باعث شده حتی یک نرم‌افزار دوباره از ابتدا نوشته شود! عدم شناخت فناوری‌های تولید نرم‌افزار (که ابزار تولید هستند)، عدم آشنایی کامل با روال گردش کارها و درک ناقص آن‌ها دو عاملی هستند که در کنار هم، آینده پروژه‌های نرم‌افزاری و توسعه آن که مهمتر و هزینه‌برتر از تولید ابتدایی هستند را با تهدید جدی روبرو می‌کنند.

تفاوت بین نگاه خوش بینانه تحلیلگران و واقعیت کار

  • عدم توجه به تجربه کاربری: یکی از تلاش‌های ما در آرایه این است که نرم‌افزار دوست داشتنی ایجاد کنیم و به کاربرانی که ساعت‌ها با نرم‌افزارهای تولیدی ما کار می‌کنند کمک کنیم تا تجربه لذت بخشی از کار با نرم‌افزار داشته باشند. ما همواره در حال یادگیری اصول مرتبط با تجربه کاربری و استفاده از آن‌ها هستیم. به کارگیری این روش، موفقیتش را در استقرار ساده‌تر سامانه‌ها و رضایت مشتریان خودش را نشان داده است. خوشبختانه در چند سال اخیر به مبحث UX توجه بیشتری شده اما هنوز در ابتدای راه هستیم. به یاد دارم دوستانی که سایت‌های وزارت خانه‌ها و سازمان‌های دولتی و اپراتورها و ... را بررسی می‌کردند و می‌کنند. متاسفانه بسیاری از نرم‌افزارها و وب‌سایت‌های دولتی هنوز ابتدایی‌ترین اصول تجربه کاربری برای دنیای امروز را رعایت نمی‌کنند.
    گاهی کار کردن با نرم‌افزارها چنان سخت و پیچیده است که کاربران حتی از همه امکانات نرم‌افزار اطلاع ندارد.
  • عدم استفاده از تست‌های نرم‌افزاری: هر جا سخن از باگ در میان است، نام عدم استفاده از Unit Test و Integration Test مناسب می‌درخشد! در این مورد شاید سری نوشته ۳۰ روز با TDD کمک کند. معتقدم عدم توجه شرکت‌های نرم‌افزاری و برنامه‌نویسان به موضوع تست، عواقب دردناکی برای آن‌ها و مشتریانشان به همراه دارد.

تصویری از Load Test یک سایت دولتی روی سرورهای اختصاصی که مشکلات نرم‌افزار استقرار یافته را نشان می‌دهد

  • ساخت نرم‌افزار پیش از اصلاح فرآیندها: حالا که از ایرادات خودمان به عنوان تولیدکنندگان نرم‌افزار گفتیم، خوب است درباره مشکل بزرگ مشتریان نرم‌افزار (اعم از دولتی و خصوصی) هم بگوییم. خیلی وقت‌ها، نرم‌افزار ایجاد می‌شود تا همان روال کاغذی و سیستم سنتی اجرای کارها، ثبت کامپیوتر شود! این همیشه کارساز نیست و بعضی وقت‌ها، اتفاقاً نه تنها روال کاغذی را از بین نمی‌برد بلکه پروسه اجرای کارها را طولانی‌تر هم می‌کند. 
    در مورد دولت (به عنوان قوه مجریه که صرفاً مجری قوانین است) دست ما برای پیشنهاد اصلاح قوانین و مقررات باز نیست اما در بخش خصوصی، بررسی پروسه‌ها و حذف بخش‌های تکراری یا زائد پیش از اجرای نرم‌افزار، به بهتر شدن تجربه نرم‌افزاری می‌انجامد.
نظر شما چیست؟ در این مورد بیشتر صحبت خواهیم کرد.

به اشتراک گذاری این نوشته در شبکه‌های اجتماعی

۳۰ روز با TDD: روز هفدهم-تعیین ترتیب اجرا در mock ها

داستان ۳۰ روز با TDD

سپتامبر سال ۲۰۱۳ آقای James Bender در وبلاگ‌های تلریک یک مجموعه نوشته منتشر کرد به نام ۳۰ روز با TDD. در ترجمه‌ای آزاد در وبلاگ آرایه، با هم در طول یک ماه با Test Driven Development آشنا می‌شویم. لینک سایر نوشته‌های این سری را در صفحه ۳۰ روز با TDD می‌توانید مشاهده کنید.

و اما هفدهیمن روز: تعیین ترتیب اجرای mock ها

امروز هم توسعه برنامه فروشگاهی که به روش TDD نوشتیم را با نگاهی نزدیک‌تر به سرویس Order Fulfillment که یک سرویس خارج از برنامه اصلی است، ادامه می‌دهیم. 

ارسال سفارش

اگر نوشته قبلی را خوانده باشید، به یاد دارید که ما با یک سرویس خارج از برنامه اصلی برای اجرای سفارشات کار می‌کردیم. آن‌ها یک API فراهم کرده‌اند و ما OrderFulfillmentService را فراخوانی می‌کنیم. interface این API چندین فراخوانی و مجموعه‌ای از قوانین برای ترتیب فراخوانی‌ها دارد. خلاصه‌ای از این interface را در کد زیر می‌توانید ببینید:

using System;
using System.Collections.Generic;




namespace TddStore.Core
{
    public interface IOrderFulfillmentService
    {
        Guid OpenSession(string user, string password);




        bool IsInInventory(Guid sessionId, Guid ItemNumber, int quantity);




        bool PlaceOrder(Guid sessionId, IDictionary items, string mailingAddress);




        void CloseSession(Guid sessionId);
    }
}

می‌توانیم درباره کارآیی این interface بحث کنیم، اما به هر حال این interface ای است که شریک اجرایی ما از آن استفاده می‌کند و بنابراین ما هم باید از همین interface استفاده کنیم. چند قانون پیرامون گردش کار این API وجود دارد:

  • کاربر باید برای هر سفارش از یک session استفاده کند
  • کاربر برای باز کردن session باید نام کاربری و کلمه عبور خود را ارائه دهد
  • همه فراخوانی‌های سرویس باید شامل شناسه (id) مربوط به session باشند
  • کاربر باید تایید کند که همه اقلام سفارش، قبل از ثبت سفارش در انبار وجود دارد. ثبت سفارش اقلامی که در انبار نیستند باعث شکست ثبت سفارش می‌شود
  • کاربر باید session را به جهت تکمیل سفارش خاتمه دهد
  • شناسه session کد رهگیری سفارش خواهد بود
چند قید (constraint) در این قوانین وجود دارد که باید روی آن‌ها کار کنیم. در نوشته امروز به همه آن‌ها نخواهیم پرداخت، اما روی ترتیب عملیات مربوط به این قیدها کار خواهم کرد. اولین چیزی که نیاز داریم یک تست کیس (test case) است
وقتی که سفارشی ثبت می‌شود که در انبار موجود است، گردش کار اجرای سفارش باید کامل شود
درست است. این یک تست کیس بسیار ساده است. این تست روی هدف نوشته امروز بسیار متمرکز شده، که در واقع اطمینان از این مطلب است که فراخوانی‌های OrderFulfillment به ترتیب انجام شوند. 
[Test]
public void WhenUserPlacesOrderWithItemThatIsInInventoryOrderFulfillmentWorkflowShouldComplete()
{
	//Arrange
	var shoppingCart = new ShoppingCart();
	shoppingCart.Items.Add(new ShoppingCartItem { ItemId = Guid.NewGuid(), Quantity = 1 });
	var customerId = Guid.NewGuid();
	var customer = new Customer { Id = customerId };




	Mock.Arrange(() => _customerService.GetCustomer(customerId)).Returns(customer).OccursOnce();
}
این‌ها موارد ابتدایی فراخوانی PlaceOrder در OrderService هستند همانطور که در نوشته قبلی اشاره کردم، باید برای دریافت اطلاعات مشتری (از جمله آدرس وی) CustomerService را فراخوانی کنم. حالا به arrange کردن mock نیازمندم. تغییر کوچکی در arrange فعلی انجام می‌دهم و بخش Act را با فراخوانی متد PlaceOrder از OrderService می‌سازم.
[Test]
public void WhenUserPlacesOrderWithItemThatIsInInventoryOrderFulfillmentWorkflowShouldComplete()
{
	//Arrange
	var shoppingCart = new ShoppingCart();
	var itemId = Guid.NewGuid();
	shoppingCart.Items.Add(new ShoppingCartItem { ItemId = itemId, Quantity = 1 });
	var customerId = Guid.NewGuid();
	var customer = new Customer { Id = customerId };
	var orderFulfillmentSessionId = Guid.NewGuid();




	Mock.Arrange(() => _customerService.GetCustomer(customerId)).Returns(customer).OccursOnce();




	Mock.Arrange(() => _orderFulfillmentService.OpenSession(Arg.IsAny(), Arg.IsAny()))
		.Returns(orderFulfillmentSessionId)
		.InOrder();




	//Act
	_orderService.PlaceOrder(customerId, shoppingCart);
	
	//Assert
	Mock.Assert(_orderFulfillmentService);
}
بیشتر کدی که نوشتم باید واضح باشد. در خط ۱۰ ، مقداری جهت ایجاد و گرفتن session id مربوط به تکمیل سفارش اضافه کردم و در خط ۲۲ اجرای صحیح mock را assert کردم. نکته جالب این کد در خط ۱۶ است که ثابت InOrder را اضافه کردم. بخش arrange کد را تکمیل می‌کنیم:
[Test]
public void WhenUserPlacesOrderWithItemThatIsInInventoryOrderFulfillmentWorkflowShouldComplete()
{
	//Arrange
	var shoppingCart = new ShoppingCart();
	var itemId = Guid.NewGuid();
	shoppingCart.Items.Add(new ShoppingCartItem { ItemId = itemId, Quantity = 1 });
	var customerId = Guid.NewGuid();
	var customer = new Customer { Id = customerId };
	var orderFulfillmentSessionId = Guid.NewGuid();




	Mock.Arrange(() => _customerService.GetCustomer(customerId)).Returns(customer).OccursOnce();




	Mock.Arrange(() => _orderFulfillmentService.OpenSession(Arg.IsAny(), Arg.IsAny()))
		.Returns(orderFulfillmentSessionId)
		.InOrder();
	Mock.Arrange(() => _orderFulfillmentService.IsInInventory(orderFulfillmentSessionId, itemId, 1))
		.Returns(true)
		.InOrder();
	Mock.Arrange(() => 
		_orderFulfillmentService.
			PlaceOrder(orderFulfillmentSessionId, Arg.IsAny>(), Arg.IsAny()))
		.Returns(true)
		.InOrder();
	Mock.Arrange(() => _orderFulfillmentService.CloseSession(orderFulfillmentSessionId))
		.InOrder();




	//Act
	_orderService.PlaceOrder(customerId, shoppingCart);
	
	//Assert
	Mock.Assert(_orderFulfillmentService);
}
در کد بالا، در خط‌های ۱۴ تا ۲۶ من mock برای OrderFulfillmentService را arrange می‌کنم. نگران نباشید و حواستان با لیست پارامترهای mock پرت نشود، با ایجاد سفارشی این پارامترها در نوشته بعدی آشنا خواهیم شد. الان فقط روی ترتیب متدها متمرکز می‌شویم. متوجه شدید که mock ها را به ترتیبی که می‌خواهم متدها اجرا شوند arrange کرده‌ام. OpenSession –> IsInInventory –> PlaceOrder –> CloseSession اگر برای فراخوانی این‌ها به ترتیب دیگری عمل کنم، تست من موقع فراخوانی MockAssert حتماً fail خواهد شد.
اگر برای اجرای این تست تلاش کنم، خطای کامپایلر برای عدم تعریف _orderFulfillmentService دریافت می‌کنم. تا اینجای کار به سرویس OrdeFulfillment نیازی نداشتم ولی حالا به mock نیاز دارم:
namespace TddStore.UnitTests
{
    [TestFixture]
    class OrderServiceTests
    {
        private OrderService _orderService;
        private IOrderDataService _orderDataService;
        private ICustomerService _customerService;
        private IOrderFulfillmentService _orderFulfillmentService;




        [TestFixtureSetUp]
        public void SetupTestFixture()
        {
            _orderDataService = Mock.Create();
            _customerService = Mock.Create();
            _orderFulfillmentService = Mock.Create();
            _orderService = new OrderService(_orderDataService, _customerService);
        }
بسیار خب. نوبت اجرای تست رسیده

تست ما fail شد چرا که هیچیک از متدهای OrderFulfillmentService  فراخوانی نشده‌اند. قدم بعدی، پیاده‌سازی کد برای این تست است
  public Guid PlaceOrder(Guid customerId, ShoppingCart shoppingCart)
{
	foreach (var item in shoppingCart.Items)
	{
		if (item.Quantity == 0)
		{
			throw new InvalidOrderException();
		}
	}




	var customer = _customerService.GetCustomer(customerId);




	//Open Session
	var orderFulfillmentSessionId = _orderFulfillmentService.OpenSession(USERNAME, PASSWORD);
	
	var firstItemId = shoppingCart.Items[0].ItemId;
	var firstItemQuantity = shoppingCart.Items[0].Quantity;
	
	//Place Order
	var orderForFulfillmentService = new Dictionary();
	orderForFulfillmentService.Add(firstItemId, firstItemQuantity);
	var orderPlaced = _orderFulfillmentService.PlaceOrder(orderFulfillmentSessionId, 
		orderForFulfillmentService, 
		customer.ShippingAddress.ToString());




	//Check Inventory Level
	var itemIsInInventory = _orderFulfillmentService.IsInInventory(orderFulfillmentSessionId, firstItemId, firstItemQuantity);




	
	//Close Session
	_orderFulfillmentService.CloseSession(orderFulfillmentSessionId);




	var order = new Order();
	return _orderDataService.Save(order);
}
در خط ۱۴ شروع کردم به کار با OrderFulFillmentService و برای اینکه بدانیم در هر مرحله چه کاری انجام می‌شود در کد، توضیح (comment) گذاشته‌ام. حالا توجه کنید که گام‌های فراخوانی من به ترتیب مورد انتظار نیست. من از باز کردن session مستقیماً به سفارش می‌روم به جای اینکه اول موجودی انبار را چک کنم. می‌توانم تلاش کنم تست را اجرا کنم ولی به دلیل نبود وهله (instance) از OrderFulfillmentService خطای کامپایل دریافت می‌کنم، همچنین سازنده کلاس را برای تزریق وابستگی به روز نکرده‌ام. حالا این دو مشکل را حل می‌کنم. در حالی که در مشغول این کار هستم، ثابت‌های نام کاربری و کلمه عبور را هم تعریف می‌کنم
namespace TddStore.Core
{
    public class OrderService
    {
        private IOrderDataService _orderDataService;
        private ICustomerService _customerService;
        private IOrderFulfillmentService _orderFulfillmentService;
        private const string USERNAME = "Bob";
        private const string PASSWORD = "Foo";
        




        public OrderService(IOrderDataService orderDataService, ICustomerService customerService, 
            IOrderFulfillmentService orderFulfillmentService)
        {
            _orderDataService = orderDataService;
            _customerService = customerService;
            _orderFulfillmentService = orderFulfillmentService;
        }
تلاش برای اجرای کد، به خطاهای کامپایل بیشتری منجر می‌شود چرا که تست من برای تزریق mock مربوط به OrderFulFillmentService  به عنوان سومین آرگومان OrderService به روز نشده. این به روز رسانی را هم انجام می‌دهم
 [TestFixtureSetUp]
public void SetupTestFixture()
{
	_orderDataService = Mock.Create();
	_customerService = Mock.Create();
	_orderFulfillmentService = Mock.Create();
	_orderService = new OrderService(_orderDataService, _customerService, _orderFulfillmentService);
}
برنامه در نهایت کامایل می‌شود. حالا نوبت اجرای تست است و مجدداً fail می‌شود!

این نتیجه غیرمنتظره نبود، اگر به یاد داشته باشید به عمد متدها را با ترتیب غیرصحیحی در OrderFulfillmentService فراخوانی کردم. نرم‌افزار (در این سری نوشته‌ها JustMock) به ما می‌گوید که ترتیب فراخوانی متدها صحیح نیست و لیست ترتیب فراخوانی را هم نشان می‌دهد. با جابجایی فراخوانی متدها به کد زیر می‌رسیم:
 public Guid PlaceOrder(Guid customerId, ShoppingCart shoppingCart)
{
	foreach (var item in shoppingCart.Items)
	{
		if (item.Quantity == 0)
		{
			throw new InvalidOrderException();
		}
	}




	var customer = _customerService.GetCustomer(customerId);




	//Open Session
	var orderFulfillmentSessionId = _orderFulfillmentService.OpenSession(USERNAME, PASSWORD);
	
	var firstItemId = shoppingCart.Items[0].ItemId;
	var firstItemQuantity = shoppingCart.Items[0].Quantity;




	//Check Inventory Level
	var itemIsInInventory = _orderFulfillmentService.IsInInventory(orderFulfillmentSessionId, firstItemId, firstItemQuantity);




	//Place Order
	var orderForFulfillmentService = new Dictionary();
	orderForFulfillmentService.Add(firstItemId, firstItemQuantity);
	var orderPlaced = _orderFulfillmentService.PlaceOrder(orderFulfillmentSessionId, 
		orderForFulfillmentService, 
		customer.ShippingAddress.ToString());
			   
	//Close Session
	_orderFulfillmentService.CloseSession(orderFulfillmentSessionId);




	var order = new Order();
	return _orderDataService.Save(order);
}
نوبت اجرای مجدد تست است. 

با اطمینان یافتن از اینکه متدها با ترتیب درستی فراخوانی می‌شوند، می‌توانم مطمئن شوم که تستم pass خواهد شد.
ادامه دارد ...
به اشتراک گذاری این نوشته در شبکه‌های اجتماعی