一步一步学NUnit
轉載:http://tech.sina.com.cn/s/2009-07-17/1129988785.shtml
單元測試基礎知識
單元測試是開發者編寫的一小段代碼,用于檢驗被測代碼的一個很小的、很明確的功能是否正確。通常而言,一個單元測試是用于判斷某個特定條件(或者場景)下某個特定函數的行為。例如,你可能把一個很大的值放入一個有序list 中去,然后確認該值出現在list 的尾部。或者,你可能會從字符串中刪除匹配某種模式的字符,然后確認字符串確實不再包含這些字符了。
執行單元測試,是為了證明某段代碼的行為確實和開發者所期望的一致。
當編寫項目的時刻,如果我們假設底層的代碼是正確無誤的,那么先是高層代碼中使用了底層代碼;然后這些高層代碼又被更高層的代碼所使用,如此往復。當基本的底層代碼不再可靠時,那么必需的改動就無法只局限在底層。雖然你可以修正底層的問題,但是這些對底層代碼的修改必然會影響到高層代碼。于是,一個對底層代碼的修正,可能會導致對幾乎所有代碼的一連串改動,從而使修改越來越多,也越來越復雜。從而使整個項目也以失敗告終。
而單元測試的核心內涵:這個簡單有效的技術就是為了令代碼變得更加完美。
NUnit介紹
NUnit 是一個免費開源的產品,它提供了一套測試框架和一個測試運行程序(test runner)。
注意:test tunner 知道如何尋找具有 [TestFixture] 屬性的類和類中的 [Test] 方法。
如何安裝 NUnit
在官網下載NUnit,當前最新的版是2.4.8,我下的是NUnit-2.4.8-net-2.0.zip。
NUnit第一個演示
我們用Visual Studio 2008新建一個NUnit項目:
| ? |
為了便于演示,我們把默認的Program.cs改成Calculator.cs,在Calculator類里,我們實現簡單的加減乘除四個方法。完整代碼如下:
using?System;namespace?NUnitTest
{
??? public?class?Calculator
??? {
??????? ///?<summary>
??????? ///?加法
??????? ///?</summary>
??????? ///?<param name="a"></param>
??????? ///?<param name="b"></param>
??????? ///?<returns></returns>
??????? public?int?Add(int?a,int?b)
??????? {
??????????? return?a?+?b;
??????? }
??????? ///?<summary>
??????? ///?減法
??????? ///?</summary>
??????? ///?<param name="a"></param>
??????? ///?<param name="b"></param>
??????? ///?<returns></returns>
??????? public?int?Minus(int?a,?int?b)
??????? {
??????????? return?a?-?b;
??????? }
??????? ///?<summary>
??????? ///?乘法
??????? ///?</summary>
??????? ///?<param name="a"></param>
??????? ///?<param name="b"></param>
??????? ///?<returns></returns>
??????? public?int?Multiply(int?a,?int?b)
??????? {
??????????? return?a?*?b;
??????? }
??????? ///?<summary>
??????? ///?除法
??????? ///?</summary>
??????? ///?<param name="a"></param>
??????? ///?<param name="b"></param>
??????? ///?<returns></returns>
??????? public?int?Divide(int?a,?int?b)
??????? {
??????????? return?a?/?b;
??????? }
??????? static?void Main(string[] args)
??????? {
??????????? Calculator cal?=?new?Calculator();
??????????? int?result?=?cal.Add(2,3);
??????????? Console.WriteLine(result);
??????????? Console.ReadKey(true);
??????? }
??? }
}
???????? 如果沒有單元測試,我們普通的測試方法就像是Main方法一樣,這樣的測試是一個很邪惡的測試方法,花時間且很難得到我們
想要的結果。
那么,我們應該如何來用NUnit做單元測試呢?
我們再新建一個項目:
| ? |
為這個NUnitTestTest引用“NUnitTest項目”和“nunit.framewor類庫”。我們再新建一個測試類,命名為“CalculatorTest.cs”。并鍵入如下代碼:
using?System;using?NUnit.Framework;
using?NUnitTest;
namespace?NUnitTestTest
{
??? [TestFixture]
??? public?class?CalculatorTest
??? {
??????? [Test]
??????? public?void TestAdd()
??????? {
??????????? Calculator cal?=?new?Calculator();
??????????? int?expected?=?5;
??????????? int?actual?=?cal.Add(2,?3);
??????????? Assert.AreEqual(expected, actual);
??????? }
??? }
}
?
這就是一個簡單的單元測試方法了。首先我們使用using NUnit.Framework和using NUnitTest,因為接下來的代碼需要用到這兩個命名空間。在這里,我們要注意幾點,NUnit測試用的類前面一定要加上[TestFixture],以表示這是NUnit測試類;測試方法一定是public的,且沒有返回值。這里的TestFixture和Test都是NUnit的Attribute,下表給出了NUnit常用的Attribute:
?
| ? |
????????? Assert.AreEqual是斷言,在測試框架中,斷言是單元測試的核心,我們在測試中要對其程序斷言。如果某個斷言失敗,方法的調用不會返回值,并且會報告一個錯誤。如果一個測試包含多個斷言,那些緊跟失敗斷言的那些斷言都不會執行,因此每個測試方法最好只有一個斷言。 NUnit.Framework.Assert有23個重載方法,大部分的情況它都有考慮到,當然,不排除需要自己寫一個復雜的斷言方法。
上面的代碼中,int expected = 5;是指我們期望程序執行的結果是5,int actual = cal.Add(2, 3);則執行Calculator.Add方法得到實際的值。
順便說一下,CalculatorTest(類名)還有TestAdd(方法名)并不是一定要這樣寫,你可以自由的命名你的名稱,不過為了讓你的代碼可讀性更好,請遵循一個命名規范,這個規范可以是公司定的也可以是網上主流的命名規則。
對Add()方法的單元測試代碼已經完成了,接下來我們運行下載解壓后文件夾中的nunit.exe,程序界面如圖:
| ? |
?
打開對話"File"/"Open Project..."對話框,或者按"Ctrl + O",把第二個單元測試項目NUnitTestTest生成的NUnitTestTest.dll加載進來:
| ? |
我們點右邊的"Run"按鈕執行單元測試:
| ? |
?
太棒了,綠色!通過!Keep the bar green to keep the code clean.
一個簡單的單元測試過程就是這樣的。
我們再為除法寫一個單元測試方法:
[Test]public?void TestDivide()
{
Calculator cal?=?new?Calculator();
int?expected?=?5;
int?actual?=?cal.Divide(25,?5);
Assert.AreEqual(expected, actual);
}
重新生成NUnitTestTest項目,NUnit會自動把TestDivide方法加進去。
| ? |
再點"Run",通過測試。大家都知道除法中除數不能為0,如果這里除數是0呢?會有什么樣的結果?
[Test]public?void TestDivide()
{
Calculator cal?=?new?Calculator();
int?expected?=?5;
int?actual?=?cal.Divide(25,?0);
Assert.AreEqual(expected, actual);
}
生成項目并重新運行單元測試:
| ? |
測試沒有通過 “NUnitTestTest.CalculatorTest.TestDivide:System.DivideByZeroException : 試圖除以零。”這時,我們要返回到Calculator類中修改Divide方法使之除數為0時返回其它的值。
NUnit第一個簡單示例就先到這里,在NUnit的官網也有簡單教程,大家可以看看。
在單元測試中,我們在做正面的測試的同時也要做一些反面測試,這樣才能讓我們的代碼更健壯。
在Visual Studio 2008 中打開上一章的示例,Calculator類有4個最簡單的方法:加、減、乘、除。CalculatorTest類中的四個方法是Calculator類四個方法的單元測試。
[TestFixture]public?class?CalculatorTest
...{
[Test]
public?void TestAdd()
...{
Calculator cal?=?new?Calculator();
int?expected?=?5;
int?actual?=?cal.Add(2,?3);
Assert.AreEqual(expected, actual);
}
[Test]
public?void TestMinus()
...{
Calculator cal?=?new?Calculator();
int?expected?=?5;
int?actual?=?cal.Minus(10,?5);
Assert.AreEqual(expected, actual);
}
[Test]
public?void TestMultiply()
...{
Calculator cal?=?new?Calculator();
int?expected?=?5;
int?actual?=?cal.Multiply(1,?5);
Assert.AreEqual(expected, actual);
}
[Test]
public?void TestDivide()
...{
Calculator cal?=?new?Calculator();
int?expected?=?5;
int?actual?=?cal.Divide(25,?5);
Assert.AreEqual(expected, actual);
}
}
這里一定要注意,TestAdd()、TestMinus()、TestMultiply()和TestDivide()方法沒有任何關系,也就是說單元測試中,所有的測試方法都是獨立的。各個方法之間沒有依賴性,刪除任何一個單元測試方法,對其它的測試不會有任何影響。
上一章中,我們已經介紹了[TestFixture]和[Test],現在我們為這個類新增一個方法。
[SetUp]public?void InitMethod()
{
Console.WriteLine("Initialization method");
}
重新生成項目,再運行NUnit,選中"CalculatorTest"進行單元測試:
| ? |
切換到NUnit的"Console.Out"中,我們看到"Initialization method"出現了4次,如果只選中一個測試方法:
| ? |
我們看到,這時只出現一次的"Initialization method"。[SetUp]的意思就是指在運行每個測試方法前執行它。相應的,有開始必然有結束,[TearDown]是指在每個測試方法結束后運行。
我們再新增一個方法:
[TearDown]public?void FinalizeMethod()
{
Console.WriteLine("Finalize method");
}
再來看運行NUnit的結果:
| ? |
知道了[SetUp]和[TearDown]后,我們就可以改寫這個單元測試類了。
請[TestFixture]public?class?CalculatorTest
...{
private?Calculator cal;
private?int?a, b, expected, actual;
[SetUp]
public?void InitMethod()
...{
cal?=?new?Calculator();
a?=?10;
b?=?2;
}
[Test]
public?void TestAdd()
...{
expected?=?12;
actual?=?cal.Add(a, b);
Assert.AreEqual(expected, actual);
}
[Test]
public?void TestMinus()
...{
expected?=?8;
actual?=?cal.Minus(a, b);
Assert.AreEqual(expected, actual);
}
[Test]
public?void TestMultiply()
...{
expected?=?20;
actual?=?cal.Multiply(a, b);
Assert.AreEqual(expected, actual);
}
[Test]
public?void TestDivide()
...{
expected?=?5;
actual?=?cal.Divide(a, b);
Assert.AreEqual(expected, actual);
}
}
因為運行每個測試方法之前,都會運行InitMethod()方法,所以每次都會初始化使第一個操作數為10,第二個操作數為2。在[SetUp]中初始化了的資源,我們就可以在[TearDown]里銷毀釋放。
這里也許有人會問,如果我的項目很大,每個測試方法都需要連接數據庫,在每個方法執行的時候進行連接再釋放,這樣是不是太耗資源太慢了,能不能在一個單元測試類實例化的時候就運行一個指定的方法呢?
這是可以的。在NUnit中,我們使用[TestFixtureSetUp]和[TestFixtureTearDown]就可以實現這樣的功能。[TestFixtureSetUp]是指在這個測試類的整個生命周期中,它在所有的測試方法之前運行一次,而[TestFixtureTearDown]是在所有的測試方法都結束時運行。
這里要注意的,[TestFixtureSetUp]與構造函數是不一樣的,它標識的方法遲于構造函數運行。我們再對這個測試類進行重構:
[TestFixture]public?class?CalculatorTest
...{
private?Calculator cal;
private?int?a, b, expected, actual;
public?CalculatorTest()
...{
Console.WriteLine("執行構造函數");
}
[TestFixtureSetUp]
public?void InitClass()
...{
Console.WriteLine("執行TestFixtureSetUp");
cal?=?new?Calculator();
a?=?10;
b?=?2;
}
[TestFixtureTearDown]
public?void FinalizeClass()
...{
Console.WriteLine("執行TestFixtureTearDown");
}
[SetUp]
public?void InitMethod()
...{
Console.WriteLine("執行SetUp");
}
[TearDown]
public?void FinalizeMethod()
...{
Console.WriteLine("執行TearDown");
a?=?10;
b?=?2;
}
[Test]
public?void TestAdd()
...{
Console.WriteLine("TestAdd() Begin");
expected?=?12;
actual?=?cal.Add(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestAdd() End");
}
[Test]
public?void TestMinus()
...{
Console.WriteLine("TestMinus() Begin");
expected?=?8;
actual?=?cal.Minus(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestMinus() End");
}
[Test]
public?void TestMultiply()
...{
Console.WriteLine("TestMultiply() Begin");
expected?=?20;
actual?=?cal.Multiply(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestMultiply() End");
}
[Test]
public?void TestDivide()
...{
Console.WriteLine("TestDivide() Begin");
expected?=?5;
actual?=?cal.Divide(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestDivide() End");
}
}
在NUnit中,我們可以很清楚地看到這個類的執行順序:
| ? |
假如我們的測試項目中有使用到數據庫,就可以把數據庫連接寫在[TestFixtureSetUp]中,把釋放的代碼寫在[TestFixtureTearDown]中。
我相信現在大家對NUnit的這4個屬性都應該有一個直觀的認識了吧。都是4個很簡單的屬性,但是在使用中用處卻是非常大的。
接下來再為大家介紹幾個常用的屬性。
現在的測試中,我們有4個測試方法,但是如果我們想讓其中的一個測試方法不在NUnit中顯示,怎么辦呢?不是注釋,大家不要想歪了,注釋大家都知道。要想讓一個測試方法不在NUnit中顯示,也不運行,我們應該使用[Ignore]屬性。看看把TestAdd()添加[Ignore]屬性后會是什么樣子:
[Test][Ignore]
public?void TestAdd()
{
Console.WriteLine("TestAdd() Begin");
expected?=?12;
actual?=?cal.Add(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestAdd() End");
}
| ? |
現在有了一個新的顏色了——黃色。它是指被忽略的方法。當然,你在項目中出現最多的肯定是綠色。在NUnit中我們可以用[Ignore]的重載方法[Ignore("忽略原因")]來定義忽略原因。
NUnit有一個與[Ignore]類似的屬性[Explicit],它是指只有在NUnit中被明確的指定時才運行,否則不運行。有點拗口,我們來看例子。改寫TestMinus方法:
[Test,Explicit]public?void TestMinus()
{
Console.WriteLine("TestMinus() Begin");
expected?=?8;
actual?=?cal.Minus(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestMinus() End");
}
這里,
和
[Test][Explicit]
是完全一樣的。
我們看它的截圖:
| ? |
"TestMinus"是灰色的,運行的Cases有2個,一個被忽略。而當我們選中TestMinus時:
| ? |
這個測試運行了。
再給大家介紹一個分類屬性[Category(string name)],利用這個分類屬性,我們可以為每個方法定義類別。
[Test, Ignore("Ignore"), Category("Category A")]public?void TestAdd()
...{
Console.WriteLine("TestAdd() Begin");
expected?=?12;
actual?=?cal.Add(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestAdd() End");
}
[Test, Category("Category B")]
[Explicit]
public?void TestMinus()
...{
Console.WriteLine("TestMinus() Begin");
expected?=?8;
actual?=?cal.Minus(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestMinus() End");
}
[Test, Category("Category A")]
public?void TestMultiply()
...{
Console.WriteLine("TestMultiply() Begin");
expected?=?20;
actual?=?cal.Multiply(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestMultiply() End");
}
[Test, Category("Category B")]
public?void TestDivide()
...{
Console.WriteLine("TestDivide() Begin");
expected?=?5;
actual?=?cal.Divide(a, b);
Assert.AreEqual(expected, actual);
Console.WriteLine("TestDivide() End");
}
重新生成項目,在NUnit中,我們可以看到:
| ? |
這里有我們定義的兩個分類,我們選中"Category A",切換回"Tests"點"Run",我們看:
| ? |
只測試了我們設置的"Category A"的一個方法,另一個方法是因為我們設置了[Ignore]所以沒有執行測試。
好,到這里,我們已經把NUnit主要的屬性學完了,接下來的章節我們將從實例出發學習NUnit。
轉載于:https://www.cnblogs.com/zhaox583132460/p/3442903.html
總結
以上是生活随笔為你收集整理的一步一步学NUnit的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: idea破解
- 下一篇: 我不想再活在别人的世界里