لزوم تست نرم افزار توسط تیم جدا

۱۰. آیا بخش تست شما جداست؟
این یعنی همان دهمین آیتم از تست جوئل.

اگر در تیم شما افرادی که وقتشان اختصاصاً برای تست کردن باشد – حداقل یک نفر برای هر دو یا سه برنامه‌نویس – وجود نداشته باشد، شما یا محصولات باگ‌دار تحویل خواهید داد؛ و یا این که با پرداخت ۱۰۰ دلار در ساعت به جای ۳۰ دلار در ساعت، پولتان را هدر می‌دهید. خساست در زمینه افراد جهت بخش تست، آن قدر صرفه‌جویی احمقانه‌ایست که من واقعاً تعجب می‌کنم چرا اکثر مردم نمی‌فهمند.

logo1

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

چرا استفاده از Thread پر هزینه است؟

در مطلب قبلی  شرح کوتاهی از Thread در ویندوز را خواندید.سیستم عامل به ازای هر Thread مقدار مشخصی از داده های ساختار یافته را تولید می کند که شامل یک سری از خواص مختص به هر Thread است.علاوه بر آن Context مربوط به هر Thread نیز فضایی را از ۳۵۰ یا ۷۰۰ و یا ۱۰۲۴ بایت را اشغال می کند.

از منابعی که به هر Thread اختصاص داده می شود می توان موارد زیر را نام برد.

Thread Information Block:این بلاک شامل یک page از memory که حاوی ذنجیره مدیریت استثنا ها و آدرس برنامه جهت دسترسی به هر چه سریعتر به أن.

user mode stack:شامل متغیر های local و همچنین شامل آدرس مشخص کننده ای برای اینکه کدام Thread باید بعد از بازگشت از متد جاری اجرا شود.

نکته:به طور پیش فرض به ازای هر Thread در ویندوز ۱ مگابایت است.

kernel mode stack:به دلایل امنیتی هر آرگومانی  از user mode  داخل kernel mode stack کپی می شود.از این کپی ها برای اعتبار سنجی کرد آرگومان ها استفاده می شود زیرا در هیچ حالتی نمی توان به kernel mode stack دسترسی داشت.

dll thread attach and detach notification:در ویندوز زمانی که یک Thread داخل یک process ساخته می شود به این صورت است که به تمام متد های main در dll های unmanage  یک فلگ به اسم dll thread attach پاس داده می شود و زمانی که Thread از بین می رود یک فلگ dll thread detach پاس داده می شود.این کار به جهت بعضی کارها مثل انجام پاکسازی ها و … که زمانگیر است.در زمان های گذشته هر process دارای ۵ یا حداکثر ۶ dll بود در حالی که در حال حاضر ویژوال استدیو که در سیستم من در حال اجراست دارای ۴۷۰ dll است که به ازای هر Thread که آغاز می شود باید ۴۷۰ فلگ به أن ها پاس داده شود و در زمان از بین رفتن آنها نیز  به همان صورت.

نکته:به dll های تولید شده توسط دات نت این فلگ ها پاس داده نمی شود.

تا به این جا متوجه شدید که باید به ازای هر thread این منابع به آن اختصاص به داده شود و کارهای بالا انجام شود.مشکل فقط این نیست و فراتر از این حرفهاست.

در کامپیوتر های تک CPU ففط یک thread حق اجرا را دارد و در این حالت باید زمان CPU بین thread ها تقسیم شود و زمان CPU در هر لحظه به یک thread اختصاص یابد و زمانی که زمان مورد نظر به پایان رسید Context جاری با thread بعدی تعویض شود که در مطلب به آن می پردازیم.

نشتی حافظه

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

test-memory-leak

در Cو C++ برنامه های تولید شده مستعد نشتی حافظه هستند زیرا عملیات آزاد سازی حافظه باید توسط برنامه نویس انجام شود ولی در دات نت چرا؟

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

  • Stack Memory:این نشتی زمانی است که منابع موجود در Stack آزاد نشود.مثلا در دات نت زمانی که شما یک تابع را صدا می زنید پارامتر های آن در Stack نگهداری می شوند حال فرض کنید این تابع هیچ وقت تمام نشود ومنابع گرفته شده از Stack آزاد نشود.
  • Unmanaged Heap Memory:فرض کنید در کد خود از کتابخانه های Unmanaged استفاده کرده باشید در این حالت اگر کتابخانه Unmanaged دچار نشتی باشد در این حال برنامه ما نیز دچار نشتی خواهد شد.
  • Managed Heap Memory:زمانی که Heap از اشیا بزرگ تشکیل شده باشد و این کار مانع فشرده سازی Heap می شود و گپ هایی در حافظه به وجود می آید.و در این حالت دچار نشتی خواهیم شد.

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

dottrace

لیستی از پروفایلر ها:

DotTrace برای شرکت سازنده Reshrper است.

 

تفاوت نتیجه گرایی و زود نتیجه گرفتن

برای همه ما پیش میاد که وقتی کاریو داریم انجام می دیم به فکر نتیجه دادن در همان دفعه اول هستیم!

مثلا شما دارید کلمه انگلیسی حفظ می کنید یا یک مقاله می خونید زمانی که کارتون تموم می شه انتظار دارید که همه چیز یادتون مونده باشه.برای من این مشکل زیاد به وجود اومده:روی یک برنامه ای کار کردم الان تو محیط عملیاتی داره کار می کنه بعد انتظارم اینه که همه چیز بار اول درست باشه و کوچیکنرین مشکلی پیش نیاد.به محض اینکه مشکلی پیش میاد  همه تمرکزم و ذهنیتم بهم می ریزه و اعتماد به نفسمو از دست می دم.حالا منظور از این همه مقدمه چینی چیزی نیست جز اینکه بخام یادآوری کنم (اول به خودم) که برای رسیدن به نتایج ایده آل شاید بارها شکست بخوریم.داستان معرفی اولین نسخه أیفون  توسط استیو جابزو در وبلاگ یک پزشک حتما بخونید.یا کار کردن روی کامپالر c# توسط eric lippert  که در مجله dotnetcurry به چاپ رسید و lippert در آن ذکرمیکنه که سالها مطالعه دقیق داشته.خود آموز برنامه نویسی در ده سال هم از این دسته مطالب است.نتیجه گرایی با زود نتیجه گرفتن متفاوت.

Result oriented

در پایان دوباره میگم بیاید به فکر نتیجه گرفتن فوری از کارامون نباشیم.

تجربه کار با Restful در WebApi

بروزرسانی:

در حال حاضر این مشکل به صورت کلی توسط مایکروسافت حل شده.

پروژه ای با ساختار زیر را فرض کنید:
Resfullهمانطور که ملاحضه می کنید در قسمت web-application فقط از کدهای کلاینت ساید استفاده شده و HTML خالص است که به وسیله درخواست هایی از نوع POST.PUT,DELETE,GET,OPTION کار خود را انجام می دهند.در این حالت اگر سایت را در یک دامین و سرویس ها را در دامین دیگری پایلیش کرده باشید اولین مشکلی که شما به آن برخواهید خورد مشکل Same-origin policy است. Same-origin policy یک policyاست که مرورگر از request به دامین دیگر جلوگیری می کند.البته مشکل GET با Jsonp برطرف خواهد شد.ولی باقی درخواست ها با مشکل زیر روبرو می شوید .XMLHttpRequest cannot load http://x.com/. Origin http://z.com is not allowed by Access-Control-Allow-Origin. راه حل این مشکل مفهومی است به اسم cors یا همان Cross-Origin Resource Sharing. اول تصمیم گرفتم از راه حل خود مایکروسافت برم که بعد از کلی مشکل به این نتیجه رسیدم که بیخیال.بعد چند تا کار انجام دادم که اون مشکل حل شد. افزودن خطوط زیر در وب کانفیگ.

  <system.webServer>

    <handlers>
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <httpProtocol>

      <customHeaders>

        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, x-xsrf-token" />
        <add name="Access-Control-Allow-Methods" value="PUT, GET, POST, DELETE" />

    </customHeaders>

    </httpProtocol>

    <validation validateIntegratedModeConfiguration="false"/>

  </system.webServer>

و نوشتن یک Ajax request از ابتدا به شکل زیر:

    createCORSRequest: function (method, url, params, success, error, sendParamInUri) {
        var data = _.getHttpParams(params);
        if (sendParamInUri) {
            url += "?" + data;
        }
        var xhr = new XMLHttpRequest();
        if ("withCredentials" in xhr) {
            xhr.open(method, _.serviceUrl + url, true);
        } else if (typeof XDomainRequest != "undefined") {
            xhr = new XDomainRequest();
            xhr.open(method, _.serviceUrl + url);
        } else {
            xhr = null;
        }
        xhr.onload = function () {
            var text = xhr.responseText;
            var data = JSON.parse(text);
            if (typeof success === 'function') {
                success(data);
            }
        };
        xhr.onerror = function () {
            var text = xhr.responseText;
            var data = JSON.parse(text);
            if (typeof error === 'function') {
                error(data);
            }

        };
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

        xhr.send(sendParamInUri == true ? "" : data);
    },

نکته استفاده از این کد withCredentials که به لینک داده شده مراجعه کنید و وظیفه آن را مشاهده کنید. مشکل بعدی هنگامی رخ دادکه سایت و سرویس ها را پاپلیش کردم متوجه شدم در local به خوبی کار میکنه ولی در iis که host می شود به مشکل برمیخورد و در هنگام درخواست میگه:method not allowed.

پس از جستجوی بسیار برای متوجه شدن درباره علت این مشکل به این جواب رسیدم که بایدWebdav را پاک کنم. نحوه پاک کردن در ویندوز سرور :

installing-and-configuring-webdav-on-iis-350-webdav_setup_1 نحوه پاک کردن در ویندوز ۷: uninstalling webdav قصدم نیست در این مطلب آموزش مفهوم نبود و فقط راه کار حل این مشکل بود.این مشکل چند روز وقت منو گرفت که امیدوارم در وقت شما صرفه جویی بشه.