۱۳۹۰/۰۱/۲۴

بررسي ميزان پوشش آزمون‌هاي واحد به كمك برنامه PartCover


هميشه در حين توسعه‌ي يك برنامه اين سؤالات وجود دارند:
- چند درصد از برنامه تست شده است؟
- براي چه تعدادي از متدهاي موجود آزمون واحد نوشته‌ايم؟
- آيا همين آزمون‌هاي واحد نوشته شده و موجود، كامل هستند و تمام عملكرد‌هاي متدهاي مرتبط را پوشش مي‌دهند؟

اين سؤالات به صورت خلاصه مفهوم Code coverage را در بحث Unit testing ارائه مي‌دهند: براي چه قسمت‌هايي از برنامه آزمون واحد ننوشته‌ايم و ميزان پوشش برنامه توسط آزمون‌هاي واحد موجود تا چه حدي است؟
بررسي اين سؤالات در يك پروژه‌ي كم حجم، ساده بوده و به صورت بازبيني بصري ممكن است. اما در يك پروژه‌ي بزرگ نياز به ابزار دارد. به همين منظور تعدادي برنامه جهت بررسي code coverage مختص پروژه‌هاي دات نتي تابحال توليد شده‌اند كه در ادامه ليست آن‌ها را مشاهده مي‌كنيد:
و ...

تمام اين‌ها تجاري هستند. اما در اين بين برنامه‌ي PartCover سورس باز و رايگان بوده و همچنين مختص به NUnit نيز تهيه شده است. اين برنامه را از اينجا مي‌توانيد دريافت و نصب كنيد. در ادامه نحوه‌ي تنظيم آن‌را بررسي خواهيم كرد:

الف) ايجاد يك پروژه آزمون واحد جديد
جهت توضيح بهتر سه سؤال مطرح شده در ابتداي اين مطلب، بهتر است يك مثال ساده را در اين زمينه مرور نمائيم: (پيشنياز: (+))
يك Solution جديد در VS.NET آغاز شده و سپس دو پروژه جديد از نوع‌هاي كنسول و Class library به آن اضافه شده‌اند:



پروژه كنسول، برنامه اصلي است و در پروژه Class library ، آزمون‌هاي واحد برنامه را خواهيم نوشت.
كلاس اصلي برنامه كنسول به شرح زير است:
namespace TestPartCover
{
public class Foo
{
public int DoFoo(int x, int y)
{
int z = 0;
if ((x > 0) && (y > 0))
{
z = x;
}
return z;
}

public int DoSum(int x)
{
return ++x;
}
}
}
و كلاس آزمون واحد آن در پروژه class library مثلا به صورت زير خواهد بود:
using NUnit.Framework;

namespace TestPartCover.Tests
{
[TestFixture]
public class Tests
{
[Test]
public void TestDoFoo()
{
var result = new Foo().DoFoo(-1, 2);
Assert.That(result == 0);
}
}
}
كه نتيجه‌ي بررسي آن توسط NUnit test runner به شكل زير خواهد بود:



به نظر همه چيز خوب است! اما آيا واقعا اين آزمون كافي است؟!

ب) در ادامه به كمك برنامه‌ي PartCover مي‌خواهيم بررسي كنيم ميزان پوشش آزمون‌هاي واحد نوشته شده تا چه حدي است؟

پس از نصب برنامه، فايل PartCover.Browser.exe را اجرا كرده و سپس از منوي فايل، گزينه‌ي Run Target را انتخاب كنيد تا صفحه‌ي زير ظاهر شود:



توضيحات:
در قسمت executable file آدرس فايل nunit-console.exe را وارد كنيد. اين برنامه چون در حال حاضر براي دات نت 2 كامپايل شده امكان بارگذاري dll هاي دات نت 4 را ندارد. به همين منظور فايل nunit-console.exe.config را باز كرده و تنظيمات زير را به آن اعمال كنيد (مهم!):
<configuration>
<startup>
<supportedRuntime version="v4.0.30319" />
</startup>

و همچنين
<runtime>
<loadFromRemoteSources enabled="true" />

در ادامه مقابل working directory‌ ، آدرس پوشه bin پروژه unit test را تنظيم كنيد.
در اين حالت working arguments به صورت زير خواهند بود (در غيراينصورت بايد مسير كامل را وارد نمائيد):
TestPartCover.Tests.dll /framework=4.0.30319 /noshadow

نام dll‌ وارد شده همان فايل class library توليدي است. آرگومان بعدي مشخص مي‌كند كه قصد داريم يك پروژه‌ي دات نت 4 را توسط NUnit بررسي كنيم (اگر ذكر نشود پيش فرض آن دات نت 2 خواهد بود و نمي‌تواند اسمبلي‌هاي دات نت 4 را بارگذاري كند). منظور از noshadow اين است كه NUnit‌ مجاز به توليد shadow copies از اسمبلي‌هاي مورد آزمايش نيست. به اين صورت برنامه‌ي PartCover مي‌تواند بر اساس StackTrace نهايي، سورس متناظر با قسمت‌هاي مختلف را نمايش دهد.
اكنون نوبت به تنظيم Rules آن است كه يك سري RegEx هستند؛ به عبارتي چه اسمبلي‌هايي آزمايش شوند و كدام‌ها خير:
+[TestPartCover]*
-[nunit*]*
-[log4net*]*

همانطور كه ملاحظه مي‌كنيد در اينجا از اسمبلي‌هاي NUnit و log4net صرفنظر شده است و تنها اسمبلي TestPartCover (همان برنامه كنسول، نه اسمبلي برنامه آزمون واحد) بررسي خواهد گرديد.
اكنون بر روي دكمه Save در اين صفحه كليك كرده و فايل نهايي را ذخيره كنيد (بعدا توسط دكمه Load در همين صفحه قابل بارگذاري خواهد بود). حاصل بايد به صورت زير باشد:
<PartCoverSettings>
<Target>D:\Prog\Libs\NUnit\bin\net-2.0\nunit-console.exe</Target>
<TargetWorkDir>D:\Prog\1390\TestPartCover\TestPartCover.Tests\bin\Debug</TargetWorkDir>
<TargetArgs>TestPartCover.Tests.dll /framework=4.0.30319 /noshadow</TargetArgs>
<Rule>+[TestPartCover]*</Rule>
<Rule>-[nunit*]*</Rule>
<Rule>-[log4net*]*</Rule>
</PartCoverSettings>

براي شروع به بررسي، بر روي دكمه Start كليك نمائيد. پس از مدتي، نتيجه به صورت زير خواهد بود:



بله! آزمون واحد تهيه شده تنها 39 درصد اسمبلي TestPartCover را پوشش داده است. مواردي كه با صفر درصد مشخص شده‌اند، يعني فاقد آزمون واحد هستند و نكته مهم‌تر پوشش 91 درصدي متد DoFoo است. براي اينكه علت را مشاهده كنيد از منوي View ، گزينه‌ي Coverage detail را انتخاب كنيد تا تصوير زير نمايان شود:



قسمت‌ نارنجي در اينجا به معناي عدم پوشش آن در متد TestDoFoo تهيه شده است. تنها قسمت‌هاي سبز را توانسته‌ايم پوشش دهيم و براي بررسي تمام شرط‌هاي اين متد نياز به آزمون‌هاي واحد بيشتري مي‌باشد.

روش نهايي كار نيز به همين صورت است. ابتدا آزمون واحد تهيه مي‌شود. سپس ميزان پوشش آن بررسي شده و در ادامه سعي خواهيم كرد تا اين درصد را افزايش دهيم.