Tóm lược NUnit

NUnit là test framework phổ biến trong cộng đồng .NET. Dù Microsoft đã tích hợp MSTest vào Visual Studio, nhiều người vẫn thích dùng NUnit. Bài này sẽ tóm tắt những tính năng thường dùng trong NUnit để tiện tra cứu.

Lệnh Assert

NUnit cung cấp hai cách để viết lệnh Assert.

Cách “cổ điển”:

1 Assert.AreEqual(4, calculator.Add(2, 2));

Cách “trôi chảy” (fluent syntax):

1 Assert.That(calculator.Add(2, 2), Is.EqualTo(4));

Cách “trôi chảy” nghe tự nhiên hơn vì nó mô phỏng một câu tiếng Anh.

So sánh kiểu string

1 Assert.That(name, Is.EqualTo("John"));
2 Assert.That(name, Is.EqualTo("John").IgnoreCase);
3 Assert.That(name, Is.Not.EqualTo("John"));

So sánh kiểu số

1 Assert.That(result, Is.EqualTo(3));
2 Assert.That(result, Is.EqualTo(3.3).Within(0.01));
3 Assert.That(result, Is.EqualTo(101).Within(1).Percent);
4 Assert.That(result, Is.Positive(3));
5 Assert.That(result, Is.Negative(3));
6 Assert.That(result, Is.NaN(3));

So sánh kiểu DateTime

1 Assert.That(result, Is.EqualTo(new DateTime(2016, 8, 21)));
2 Assert.That(result, Is.EqualTo(new DateTime(2016, 8, 21))
3                       .Within(TimeSpan.FromMillisecond(1)));

Ta truyền vào Within() một đối tượng kiểu TimeSpan. Kiểu này đóng vai trò là sai số (tolerance).

1 Assert.That(result, Is.EqualTo(new DateTime(2016, 8, 21))
2                       .Within(1).Milliseconds);

So sánh một khoảng giá trị (range)

1 Assert.That(result, Is.GreaterThan(100));
2 Assert.That(result, Is.InRange(100, 200)); // inclusive
3 Assert.That(result, Is.GreaterThanOrEqualTo(100));
4 Assert.That(result, Is.LessThan(100));
5 Assert.That(result, Is.LessThanOrEqualTo(100));

So sánh giá trị Null và Boolean

1 Assert.That(name, Is.Not.Empty);
2 Assert.That(name, Is.Null);
3 Assert.That(name, Is.Not.Null);
4 Assert.That(name, Is.True);
5 Assert.That(name, Is.False);

So sánh collection

 1 // No item in string collection is empty
 2 Assert.That(stringCollection, Is.All.Not.Empty);
 3 
 4 // Any item in collection with value "Name"?
 5 Assert.That(stringCollection, Contains.Item("Name"));
 6 
 7 // One or more items in collection contains the substring "sub"
 8 Assert.That(stringCollection, Has.Some.ContainSubString("sub"));
 9 
10 // The collection contains exactly 2 items with value "Name"
11 Assert.That(stringCollection, Has.Exactly(2).EndsWith("Name"));
12 
13 // The collection has no duplicate
14 Assert.That(stringCollection, Is.Unique);
15 
16 Assert.That(stringCollection, Has.None.EqualTo("Name"));
17 
18 // Check if 2 collections contain the same elements, won't check order
19 Assert.That(stringCollection, Is.EquivalentTo(anotherCollection));
20 
21 // Is the collection alphabetically ordered?
22 Assert.That(stringCollection, Is.Ordered);

So sánh tham chiếu

1 // Are 2 variables point to the same object?
2 Assert.That(object1, Is.SameAs(object2));
3 
4 Assert.That(object1, Is.Not.SameAs(object2));

Kiểm tra kiểu đối tượng và thuộc tính

1 Assert.That(object, Is.TypeOf<Type>());
2 Assert.That(object, Is.InstanceOf<Type>());
3 Assert.That(object, Has.Property("Name"));

Kiểm tra ngoại lệ (exception)

 1 // Not a good idea, should be more specific
 2 Assert.That(() => Calculator.Divide(2, 0), Throws.Exception);
 3 
 4 Assert.That(() => Calculator.Divide(2, 0),
 5                   Throws.TypeOf<DivideByZeroException>());
 6 
 7 Assert.That(() => Calculator.Divide(2, 0),
 8                   Throws.TypeOf<ArgumentOutOfRangeException>()
 9                         .With.Matches<ArgumentOutOfRangeException>(
10                              x => x.ParaName == "value"));

Attribute thường dùng

Chạy code trước và sau mỗi test

 1 private Calculator calculator;
 2 
 3 [SetUp]
 4 public void BeforeEachTest()
 5 {
 6     calculator = new Calculator();
 7 }
 8 
 9 [TearDown]
10 public void AfterEachTest()
11 {
12     // clear, release resources
13 }

Ta có thể truy xuất thông tin của test thông qua đối tượng TestContext.CurrentContext.Test. Giả sử tôi muốn lấy tên của test method, tôi dùng dòng lệnh sau:

1 Console.WriteLine(TestContext.CurrentContext.Test.Name);

Chạy code trước và sau một test fixture

1 [TestFixtureSetUp]
2 public void BeforeAnyTestStarted() {}
3 
4 [TestFixtureTearDown]
5 public void AfterAllTestsFinished() {}

Chạy code trước và sau assembly hoặc namespace

 1 namespace Calculator.Tests
 2 {
 3     [SetUpFixture]
 4     public class SetUpFixtureForNamespace
 5     {
 6         [SetUp]
 7         public void RunBeforeAnyTestInNamespace() {}
 8 
 9         [TearDown]
10         public void RunAfterAnyTestInNamespace() {}
11     }
12 }

Nếu không có dòng khai báo namespace thì [SetUpFixture] sẽ áp dụng cho tất cả test trong assembly.

1 [SetUpFixture]
2 public class SetUpFixtureForEntireAssembly
3 {
4     [SetUp]
5     public void RunBeforeAnyTestInAssembly() {}
6 
7     [TearDown]
8     public void RunAfterAnyTestInAssembly() {}
9 }

Chạy test với nhiều dữ liệu test case

1 [TestCase(2, 2, 4)]
2 public void ShouldAdd(int num1, int num2, int expected) {}

Có thể dùng nhiều test case:

1 [TestCase(1, 2, 3)]
2 [TestCase(2, 2, 4)]
3 [TestCase(4, 3, 7)]
4 public void ShouldAdd(int num1, int num2, int expected) {}

Tái sử dụng dữ liệu test case

Tạo một class để cung cấp dữ liệu cho test method.

1 public class TestCaseSource : IEnumerable
2 {
3     public IEnumerator GetEnumerator()
4     {
5         yield return new[] { 1, 1, 2 };
6         yield return new[] { 2, 2, 4 };
7         yield return new[] { 3, 4, 7 };
8     }
9 }
1 [TestCaseSource(typeof(TestCaseSource))]
2 public void ShouldAdd(int num1, int num2, int expected) {}

Tạo tổ hợp dữ liệu đầu vào

1 [Test]
2 public void ShouldAddAndDivide([Values(10, 20, 30)] int numberToAdd,
3                                [Values(2, 1, 0)] int numberToDivide) {}

Bỏ qua test

1 [Test]
2 [Ignore] // Shown as a warning sign in Test Explorer
3 public void ShouldAdd() {}

Phân nhóm test bằng Category

1 [Test]
2 [Category("CategoryName")] // Can also be used for TestFixture
3 public void ShouldAdd() {}

Trong Test Explorer, ta chọn mục Traits để hiển thị ra các nhóm.

Chỉ định thời gian chạy test tối đa

1 [Test]
2 [MaxTime(4000)] // milliseconds
3 public void ShouldAdd() {}

Test sẽ fail nếu thời gian thực thi test method quá thời gian quy định (4 giây).

Chạy một test nhiều lần

1 [Test]
2 [Repeat(10000)]
3 public void ShouldDoWork() {}

Trong số 10,000 lần chạy, chỉ cần 1 lần fail là test sẽ fail.