การพัฒนา Unit Testing โดย xUnit test library (.NET Core)

บทความนี้นำเสนอขั้นตอนการพัฒนา Unit Test อย่างง่ายโดยแสดงให่้เห็นการพัฒนาทีละขั้นตอน สำหรับระบบที่พัฒนาบน .NET Core โดยใช้ xUnit เพื่อทำความเข้าใจการพัฒนาแบบ test-driven development (TDD) ซึ่งในขั้นแรก เริ่มต้นด้วยการสร้าง source project ที่จะใช้สำหรับการทดสอบ สร้าง folder “TestSample” และ sub folder ชื่อ “CalcTool”  จากนั้นเปิด command prompt เข้าไปที่ folder “CalcTool” เพื่อทำการสร้าง .NET Core project โดยใช้คำสั่ง dotnet new classlib rename class1.cs เป็นชื่อ calc.cs และเขียน code ดังนี้ using System; namespace CalcTool { public class Calc { public int AbsAddByOne(int value) { throw new NotImplementedException(“Not implemented”); } } } สร้าง test project โดยกลับไปที่ folder “TestSample” และสร้าง sub folder ชื่อ “CalcTool.Tests” จากนั้นเปิด command prompt เข้าไปที่ folder “CalcTool.Tests” เพื่อทำการสร้าง .NET Core project โดยใช้คำสั่ง dotnet new xunit โดยคำสั่งนี้จะทำการสร้าง test project ที่ใช้ xunit test library และกำหนดค่า test runner ไว้ใน CalcTool.Tests.csproj file : CalcTool.Tests.csproj <Project Sdk=”Microsoft.NET.Sdk”> <PropertyGroup> <TargetFramework>netcoreapp1.1</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include=”Microsoft.NET.Test.Sdk” Version=”15.0.0″/> <PackageReference Include=”xunit” Version=”2.2.0″/> <PackageReference Include=”xunit.runner.visualstudio” Version=”2.2.0″/> </ItemGroup> </Project> เพิ่ม reference ไปยัง project ที่ต้องการทดสอบซึ่งในที่นี้คือ CalcTool project โดยใช้คำสั่ง dotnet add reference ../CalcTool/CalcTool.csproj ก่อนที่จะ build CalcTool project หรือ CalcTool.Tests project ต้อง execute คำสั่ง dotnet restore เพื่อ restore NuGet package ที่จำเป็นต้องใช้ในแต่ละ project เริ่มพัฒนา unit testing โดยลบ file “UnitTest1.cs” และสร้าง file ใหม่โดยใช้ชื่อว่า “CalcTest.cs” และเขียน code ดังนี้ using System; using Xunit; using CalcTool; namespace CalcTool.Tests { public class CalcTest { [Fact] public void AbsAddByOneTest() { var c = new Calc(); var result = c.AbsAddByOne(5); Assert.Equal(result, 6); } } } *[Fact]

Read More »

การสร้างเอกสารด้วย Sphinx

sphinx คือเครื่องมือที่ช่วยในการสร้างเอกสารที่พัฒนาโดย Georg Brandl เดิมที sphinx ได้รับการพัฒนาเพื่อใช้สร้างเอกสารสำหรับ Python แต่สามารถใช้งานกับภาษาอื่นๆได้เช่นกัน sphinx ใช้ reStructuredText (ดูข้อมูลเพิ่มเติมเกี่ยวกับ reStructuredText ได้ที่ บทความ ก่อนหน้า) ในการกำหนดรูปแบบของเอกสาร และชุดเครื่องมือในการ parsing และ translating เอกสารในรูปแบบ  reStructuredText ไปเป็นรูปแบบที่ต้องการเช่น html หรือ pdf เป็นต้น ในการติดตั้งใช้งาน sphinx นั้นจำเป็นต้องต้องติดตั้ง Python เนื่องจาก sphinx ได้รับการพัฒนาโดยใช้ Python language ทำให้การใช้งาน sphinx ต้องติดตั้ง Python ด้วย โดย Python ที่ใช้ต้องเป็น Python version 2.7 เป็นอย่างน้อย การติดตั้ง Python บน windows  ทำได้โดยเข้าไป download “Python windows installer” ที่ https://www.python.org/ และทำการติดตั้ง หลังจากติดตั้งเรียบร้อย จะต้องทำการกำหนดค่า Python executable directory ใน PATH environment variable เพื่อที่จะสามารถ run Python และ package command เช่น sphinx-build ได้จาก command prompt Installing Sphinx with pip การติดตั้ง Sphinx ทำได้โดยการใช้ “pip”  ซึ่ง pip เป็นเครื่องมือที่ใช้ในการ download และติดตั้ง 3rd-party libraries สำหรับ Python ซึ่งจะถูกรวมอยู่ใน Python official installation ตั้งแต่ version Python-3.4.0 การติดตั้ง sphinx โดยใช้คำสั่งดังนี้บน command prompt C:\> pip install sphinx หลังจากติดตั้งเรียบร้อย ให้พิมพ์คำสั่ง sphinx-build -h บน command prompt ถ้าทุกอย่างถูกต้อง จะแสดงข้อมูล Sphinx version number และ list ของ option ต่างๆสำหรับคำสั่งนี้ Setting up the documentation sources การพัฒนาเอกสารด้วย sphinx นั้น เริ่มแรกเราจะต้องกำหนดพื้นที่สำหรับพัฒนาเอกสารและจัดเก็บ config ที่ใช้สำหรับ sphinx ซึ่ง sphinx มีคำสั่ง sphinx-quickstart ซึ่งทำหน้าที่กำหนด source directory และสร้าง default config file “conf.py” ที่จำเป็นให้ โดยใช้คำสั่งดังนี้ C:\> sphinx-quickstart sphinx-quickstart script สร้างโครงสร้าง folder พร้อมทั้ง file เริ่มต้นรวมทั้ง Makefile และ make.bat ซึ่งจะใช้ในการ build (parsing และ translating โดยที่ถ้าพบส่วนที่ไม่ตรงตามข้อกำหนด syntax จะแสดง warning หรือ error พร้อมทั้งรายละเอียดของ line เช่นเดียวกับการ build “code program”) หลังได้โครงสร้าง folder สำหรับพัฒนาเอกสารเรียบร้อย ก็สามารถเริ่มต้นเขียนเอกสารโดยใช้ reStructuredText ในการกำหนดรูปแบบการแสดงผลของ text จากนั้นเมื่อต้องการสร้างเอกสารในรูปแบบที่ต้องการเช่น html document ก็จะต้องทำการ build โดยใช้คำสั่ง C:\> make html ผลลัพธ์ที่ได้จากการใช้คำสั่งนี้คือ HTML

Read More »

การกำหนดรูปแบบเอกสารด้วย reStructuredText

การสร้างเอกสารด้วย reStructuredText syntax ทำให้เอกสารที่พัฒนาง่ายในการอ่าน จัดรูปแบบได้ตามที่เห็น รูปแบบของ markup เป็น plaintext ซึ่งจะใช้ parser ในการแปลงให้อยู่ในรูปแบบเอกสารที่ต้องการเช่น html หรือ pdf เป็นต้น การกำหนดรูปแบบ (Text Formatting) 1. Paragraphs Paragraphs คือ block พื้นฐานของ reStructuredText ซึ่งแต่ละ paragraphs จะถูกแยกด้วย blank line 1 บรรทัด หรือมากกว่าหนึ่งก็ได้ ซึ่งย่อหน้ามีผลกับ reStructuredText ดังนั้นในการกำหนดบรรทัดใน paragraph เดียวกันจะต้องมี ย่อหน้าด้สนซ้ายเท่ากันในระดับเดียวกัน. 2. Inline markup และ special character เราสามารถใช้ตัวอักษรพิเศษในการกำหนดรูปแบบข้อความมีอยู่ด้วยกันหลายตัว  ตัวอักษรพิเศษ * ถูกใช้ในการกำหนดรูปแบบตัวอักษรตัวหนา และ ตัวอักษรตัวเอียง ดังตัวอย่างด้านล่าง ตัวหนา **ภาษาไทย**  จะแสดงผลคือ  ภาษาไทย ตัวเอียง *ภาษาไทย*   จะแสดงผลคือ  ภาษาไทย ตัวอักษรพิเศษ backquote  “  จะใช้ในการกำหนดตัวอย่าง  code โดยการใช้งานดังนี้ “code sample“ แต่ถ้าต้องการใช้ * และ `  ในเอกสารด้วยซึ่งจะทำให้สับสนกับ inline markup ได้ ดังนั้นจึงใช้  backslash นำหน้าสำหรับ * และ ` ที่จะใช้แสดงผลของเอกสาร 3. Lists and Quote-like blocks การกำหนด list สามารถทำได้โดยการใช้ * นำหน้า * bulleted list * bulleted list * bulleted list สำหรับ numbered list ก็ให้ใช้ตัวเลขนำหน้า 1. numbered list 2. numbered list ถ้าต้องการ list ที่กำหนดเลขโดยอัตโนมัติให้ใช้ # นำหน้า #. numbered list #. numbered list การกำหนด list ซ้อนกันหลายๆชั้นสามารถทำได้ แต่ต้องแยกแต่ละ list ออกด้วย blank line *. bulleted list *. bulleted list *. bulleted list *. bulleted list *. bulleted list 4. Source code การกำหนด code block ทำได้โดยใช้ special marker :: ที่ตอนจบของ paragraph และจะต้องแยกจากส่วนอื่นด้วย blank line เช่นเดียวกับ paragraph ทั่วไป This is a normal text paragraph. The next paragraph is a code sample:: public class reStructuredTest { public reStructuredTest(){ } } 5. Table การสร้างตารางทำได้ในสองรูปแบบคือแบบ grid table และ simple table โดยการสร้างตารางแบบ grid table ทำได้ด้วยการวาดตารางด้วยตัวเอง +————————+————+———-+———-+ | Header row, column 1 | Header 2

Read More »

Unit test สำหรับ method ที่ใช้ Entity Framework

กลไกการทำงานของ .NET method โดยส่วนใหญ่  จะมีกลไกการทำงานที่ต้องขึ้นกับเงื่อนไข ปัจจัยต่างๆจากภายนอกอย่างเช่น method ที่มีเรียกใช้ database หรือ web service ทำให้การทดสอบจำเป็นต้องแยก component ที่ต้องการทดสอบออกมาจากปัจจัยแวดล้อมต่างรอบๆ component ซึ่งสามารถทำได้โดยใช้ shim ที่อยู่ภายใต้การควบคุมในกระบวนการทดสอบ สามารถที่จะควบคุมผลลัพธ์ที่ได้การทำงานตามที่กำหนดในทุกๆครั้งที่เรียกใช้งาน ซึ่งทำให้การเขียน unit testing ทำได้ง่ายขึ้นมาก การพัฒนา unit test สำหรับ method ที่ใช้ Entity Framework เพื่อเข้าถึงฐานข้อมูล ก็สามารถใช้ shim type ในการกำหนดชุดของข้อมูลเพื่อทำการทดสอบ ซึ่งการ query จะกระทำกับ property ของ DbContext ซึ่ง return IDbSet<T> public partial class Entities : DbContext { public Entities(): base(“name=Entities”) { } public IDbSet<CONFIG> CONFIG { get; set; } … } ในการพัฒนา unit test จะต้องสร้าง shim type สำหรับ class “Entities” และแทนที่ property ที่ดึงข้อมูลจากฐานข้อมูลจริงแยกออกจากการทดสอบ ด้วยข้อมูลสำหรับทดสอบ จากตัวอย่างข้างบนคือ property “CONFIG” ซึ่งเป็นประเภท IDbSet<CONFIG> โดยจะทำการ returm DbSet<CONFIG> ที่เตรียมข้อมูลไว้สำหรับการทดสอบ using (ShimsContext.Create()) { ShimEntities.AllInstances.CONFIGGet = (e) => { return … DbSet<CONFIG> }; } แต่ใน DbSet ไม่มี public constructor ทำให้ไม่สามารถสร้าง instance ของ DbSet ได้ จึงจำเป็นต้องสร้าง class ใหม่ที่ implement interface IDbSet<T> แทนการใช้ DbSet public class TestDbSet<T> : IDbSet<T>, IQueryable, IEnumerable<T> where T : class { ObservableCollection<T> _data; IQueryable _query; public TestDbSet() //: base() { _data = new ObservableCollection<T>(); _query = _data.AsQueryable(); } public virtual T Find(params object[] keyValues) { throw new NotImplementedException(“Derive from TestDbSet<T> and override Find”); } public T Add(T item) { _data.Add(item); return item; } public T Remove(T item) { _data.Remove(item); return item; } public T Attach(T item) { _data.Add(item); return item; } public T Create() { return Activator.CreateInstance<T>(); } public TDerivedEntity

Read More »

การพัฒนา unit test โดย shim type

Shims เป็นหนึ่งใน technology ที่อยู่ใน Microsoft Fakes Framework ใช้ในการพัฒนา unit testing เพื่อแยก component ที่ต้องการทดสอบออกมาจากปัจจัยแวดล้อมต่างรอบๆ component ในกระบวนการทดสอบ โดย shims จะทำการเปลี่ยนทิศทางการเรียกใช้ method ที่กำหนด ไปยัง code ที่เขียนขึ้นมาใช้ในการทดสอบ ส่วนใหญ่เราจะใช้ shims เพื่อแยก component ที่ต้องการทดสอบออกจาก assemblies ที่ไม่ได้เป็นส่วนหนึ่งของ solution ในการพัฒนา (กรณีที่ต้องการแยก component ที่ต้องการทดสอบออกจาก solution ของตัวเอง ควรจะใช้ stubs ) method ที่พัฒนาส่วนใหญ่จะ return ผลการทำงานที่ต้องขึ้นกับเงื่อนไข ปัจจัยต่างๆจากภายนอก ในทางกลับกันสำหรับ shim  shim จะอยู่ภายใต้การควบคุมในกระบวนการทดสอบ สามารถที่จะ return ผลการทำงานตามที่กำหนดในทุกๆครั้งที่เรียกใช้งาน ซึ่งทำให้การเขียน unit testing ทำได้ง่ายขึ้นมาก ตัวอย่าง method การตรวจสอบวันที่เอกสารในปีงบประมาณ public static class Utility { public static bool IsInFiscalYear() { if (DateTime.Now < new DateTime(2015, 10, 1)) return false; else return true; } } เมื่อต้องการทดสอบ method “IsInFiscalYear” จะพบว่าการทำงานของ method ขึ้นอยู่กับ DateTime.Now ซึ่งเป็นเวลาปัจุจบันที่ได้จากระบบ ซึ่งทำให้การทดสอบยุ่งยากขึ้น (เมื่อทำการทดสอบต้องเปลี่ยน DateTime เพื่อทำการทดสอบ ซึ่งอาจจะกระทบกับส่วนอื่นๆ ไม่สามารถทำการทดสอบแบบอัตโนมัติได้ ) ซึ่งการทดสอบ method ที่มีการเรียกใช้ database, web service ก็เช่นเดียวกัน เนื่องจากกลไกการทำงานขึ้นอยู่กับปัจจัยภายนอก ซึ่ง shim จะเข้ามาช่วยตรงจุดนี้ Shim types จะให้กลไกการเปลี่ยนทิศทางการเรียกใช้ .NET method ไปยัง function ( หรือ user delegate) ที่เขียนขึ้น โดย shim types จะถูกสร้างโดย Fakes generator using (ShimsContext.Create() { ShimDateTime.NowGet = () => new DateTime(2016, 6, 1); var isIn = Utility.IsInFiscalYear(); Assert.AreEqual(true, isIn); } ShimDateTime คือ shim type ที่ถูกสร้างโดย Fakes generator เพื่อใช้ในการกำหนดกลไกการทำงานแทน DateTime.Now การเพิ่ม Fakes Assemblies ใน solution ทำได้โดย ใน solution explorer ขยาย References ของ unit test project เลือก assembly ที่มี class ที่ต้องการสร้าง shim type (จากตัวอย่างนี้ต้องการสร้าง shim type ของ DateTime ให้เลือก System.dll) click ขวา เลือก Add Fakes Assembly ในการใช้ shim type ใน unit test framework จะต้องเขียน test code อยู่ใน ShimsContext เพื่อควบคุม lifetime

Read More »