[Tutorial] Viết Unit Test trong C# với NUnit

Giới thiệu tổng quan về Unit Test

trường đại học chắc các bạn đã được học khái niệm về Unit Test trong môn “Kiểm thử chất lượng phần mềm”. Nói một cách dễ hiểu, unit test tức là code dùng để test code ta đã viết.

Một số đặc điểm của unit test:

  1. Code unit test phải ngắn gọn, dễ hiểu, dễ đọc.
  2. Mỗi unit test là 1 đơn vi riêng biệt, độc lập, không phụ thuộc vào unit khác.
  3. Mỗi unit test là 1 method trong test class, tên method cũng là tên UnitTest. Do đó ta nên đặt tên hàm rõ ràng, nói rõ unit test này test cái gì (Test_A_Do_B), tên method có thể rất dàiii cũng không sao.
  4. Unit Test phải nhanh, vì nó sẽ được chạy để kiểm định lỗi mỗi lần build. Do đó trong unit test nên hạn chế các task tốn thời gian như gọi I/O, database, network,…
  5.  Unit Test nên test từng đối tượng riêng biệt. Vd: Unit Test cho Business Class thì chỉnh test chính BusinessClass đó, không nên dụng tới các class móc nối với nó (DataAccess Class chẳng hạn).

Một số bạn sẽ thắc mắc: “Ơ hay, trong business class thì phải gọi data access chứ?”, và “code mình dính chùm lắm, làm sao test từng class riêng được”.

  • Để viết unit test, các class của bạn phải có quan hệ “lõng lẽo” với nhau (loose coupling), nếu không viết được unit test nghĩa là các class dính với nhau quá chặt, sẽ khó thay đổi sau này. Hãy áp dụng các nguyên lý SOLID vào code, viết code với tư tưởng “viết sao để unit test được” sẽ là code của bạn uyển chuyển, dễ test hơn.
  • Về vấn đề Business Class và Data Access, các class không nên gọi trực tiếp lẫn nhau mà gọi thông qua interface. Khi unit test, ta sẽ thay các hiện thực interface này bằng các class giả, thay thế cho class data access thật. Mình sẽ nói rõ về vấn đề này ở những bài viết về Inversion of Control, Dependency Injection trong tương lai.

Một số lợi ích của Unit Test:

  • Nếu viết Unit Test một cách cẩn thận, code của bạn sẽ ít lỗi hơi, vì Unit Test sẽ phát hiện lỗi cho bạn.
  • Phát hiện những hàm chạy chậm và không hiệu quả thông qua thời gian chạy của Unit Test.
  • Tăng sự tự tin khi code, vì đã có Unit Test phát hiện lỗi.
  • Khi refactor code, sửa code hay thêm chức năng mới, Unit Test đảm bảo chương trình chạy đúng, phát hiện những lỗi tiềm tàng mà ta bỏ lỡ.

unit-test-as-first-class-language-construct

Gần đây, mô hình phát triển TDD (Test Driven Development) đang trở nên hot, được áp dụng nhiều. Mô hình này dựa trên khái niệm: Với mỗi chức năng, ta viết Unit Test trước, sau đó viết hàm hiện thực chức năng để unit test pass. Một số công ty ở Việt Nam cũng đang áp dụng mô hình này, trong khi phỏng vấn xin việc cũng có nhé :D.

Các bạn có thể xem thêm về Unit Test và TDD ở đây: http://www.pcworld.com.vn/articles/cong-nghe/cong-nghe/2005/12/1188434/unit-test-voi-phat-trien-phan-mem-hien-dai/

Unit Test trong C# với NUnit

Trước đây, để viết Unit Test trong C#, ta thường phải tạo một project test riêng, sử dụng thư viện MSTest của Microsoft. MSTest hỗ trợ khá nhiều chức năng: Test dữ liệu từ database, đo performance hệ thống, xuất dữ liệu report. Tuy nhiên, do MSTest đi kèm với Visual Studio, không thể chạy riêng rẽ, lại khá nặng nề, do đó NUnit được nhiều người ưa thích hơn. NUnit có 1 bộ runner riêng, có thể chạy UnitTest độc lập không cần VisualStudio, ngoài ra nó cũng hỗ trợ một số tính năng mà MSTest không có (parameter test, Assert Throw).

Nếu bạn chưa bao giờ viết Unit Test, bắt đầu với NUnit cũng là 1 lựa chọn không tồi. Đầu tiên, ta hãy tạo 1 project console trong Visual Studio. Ta viết 1 class Calculator với các 2 hàm Add và Subtract:

    public class Calculator
    {
        public int Add(int x, int y)
        {
            return x + y;
        }

        public int Subtract(int x, int y)
        {
            return x - y;
        }

    }

Bây giờ, ta sẽ viết UnitTest để test 2 hàm này. Thông thường, code Unit Test sẽ được nằm ở một project Test riêng biệt, do đó ta sẽ tạo 1 project mới, thêm “.Test” đằng sau tên. Các bạn nhớ chọn kiểu project là Class Library nhé.
1Ở project mới, bấm vào Tools -> Library Package Manager -> Package Manager Console, gõ vào: `Install-Package NUnit -Version 2.6.4` để cài đặt NUnit nhé. Ta tạo 1 class mới có tên là CalculatorTest. Bắt đầu viết thôi:

    using NUnit.Framework;

    [TestFixture]
    public class CalculatorTest
    {
        private Calculator _cal;

        [SetUp]
        public void Setup()
        {
            _cal = new Calculator();
        }

        [Test]
        public void OnePlusOneEqualTwo()
        {
            Assert.AreEqual(2, _cal.Add(1, 1));
        }

        [Test]
        public void TwoPlusTwoEqualFour()
        {
            Assert.AreEqual(4, _cal.Add(2, 2));
        }

        [Test]
        public void FourPlusOneEqualFive()
        {
            Assert.AreEqual(5, _cal.Add(4, 1));
        }
    }

Giải thích một số annotation:

  • Annotation [TextFixture] đặt vào đầu class chứa các unit test, đánh dấu đây là một bộ các unit test.
  • Annotation [SetUp] để đánh dấu hàm setup. Hàm này sẽ được chạy vào đầu mỗi unit test.
  • Annotation [Test] để đánh dấu hàm bên dưới là một unit test. Nhiều khi bạn viết unit test xong nhưng không thấy hiện unit test lên là do quên annotation này nhé.

Để chạy Unit Test, ta cần cài NUnit.TestAdapter, đây là bộ runner cho phép chạy các Unit Test của NUnit trong Visual Studio. Bạn cũng có thể dùng 1 trong 2 cách sau:

  1. Tải NUnit GuiRunner về, mở file dll của project test, các unit test sẽ hiện ra để chạy.
  2. Tải Reshaper. Reshaper cũng tích hợp sẵn 1 bộ Runner cho phép chạy Unit Test của NUnit.

Để cài TestAdapter cho Visual Studio, ta vào Tool, Extentions and Updates, tìm NUnit và cài đặt. Cài xong nhớ Restart lại Visual Studio nhé.

2

Bấm Test -> Run -> All Tests để bắt đầu chạy. Ta sẽ thấy toàn bộ các case đã pass, màu xanh hiền hòa như trong ảnh.

3

Ta cố ý sửa lại code để code chạy sai. Chạy lại Unit Test, ta sẽ thấy các case đã fail. Unit Test đã tự động bắt lỗi chương trình cho bạn rồi đấy. Bấm vào từng test case, ta sẽ thấy kết quả mong muốn ở đoạn “Excepted“, kết quả code chạy ở đoạn “But was“.

4

Mục đích của bài viết là hướng dẫn các bạn nhập môn việc viết Unit Test cho C# bằng NUnit nên mình sẽ dừng ở đây. Nếu được ủng hộ, mình sẽ giới thiệu một số kĩ thuật phức tạp hơn như: Sử dụng Mock Object, Parameterlized Test, … ở những bài sau. Bạn nào muốn viết unit test cho javascript có thể thử tìm hiểu về Jasmine, mình đã có 2 bài giới thiệu tổng quát. Cảm ơn và hẹn gặp lại.

 

13 thoughts on “[Tutorial] Viết Unit Test trong C# với NUnit”

  1. cái này có còn quan trọng lắm không bác, anh làm chỗ em nói là cái này cũ rồi, đừng học nữa. 😀

    Like

      1. sr bác, cái này khá quan trọng với các hệ thống phát triển lâu dài, nhưng công ty em chuyên làm các web ngắn hạn nên mới ko cần lắm. các bạn nên học thêm mấy cái này nhá dù sao biết thêm được nhiều thứ cũng là tốt. 🙂

        Like

      2. Thật ra anh ấy nói đúng đấy bạn. Cái việc viết Unit Test này khá mất thời gian, có khi bằng hoặc hơn thời gian viết code. Với các dự án cần bảo trì thì nó khá cần thiết, nhưng áp dụng nó vào các dự án nhỏ lại làm mất thời gian, ảnh hưởng tiến độ lắm :D.

        Liked by 1 person

      3. oke bác, xác định là developer thì mấy cái này trước sau gì cũng phải biết thôi. rất vui được trao đổi với bác.

        Like

  2. anh cho em hỏi muốn tìm hiểu về unit test thì bắt đầu tư đâu,anh biết có sách vở hay tài liệu gì để có thể tự học đc ko.

    ps :cảm ơn anh nhiều.

    Like

  3. Hi bạn, bạn có biết làm sao để mock được database (DBContext) không? Mình đang làm Entity framework và đang muốn test tầng DAO nên cần mock DbConext.

    Like

Leave a comment