1、單元測試是什麼php
單元測試(unit testing),是指對軟件中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,通常來講,要根據實際狀況去斷定其具體含義,如C語言中單元指一個函數,C#裏單元指一個類,圖形化的軟件中能夠指一個窗口或一個菜單等。總的來講,單元就是人爲規定的最小的被測功能模塊。單元測試是在軟件開發過程當中要進行的最低級別的測試活動,軟件的獨立單元將在與程序的其餘部分相隔離的狀況下進行測試。 python
單元測試(模塊測試)是開發者編寫的一小段代碼,用於檢驗被測代碼的一個很小的、很明確的功能是否正確。一般而言,一個單元測試是用於判斷某個特定條件(或者場景)下某個特定函數的行爲。 程序員
2、爲何須要單元測試 編程
在咱們如今的編程思惟中一直都是編碼=>編譯=>調試,一直循環,直到要處理的功能完成,每個功能完成都是如此,且有的功能是嚴重依賴於上一個功能。在如此處理中存在幾個問題。 數組
有了單元測試在開發過程當中起到的做用。 多線程
既然單元測試有這些好處,爲何咱們不去用呢。能夠概括爲如下幾個理由。 框架
答:在開發時越早發現bug,就能節省更多的時間,下降更多的風險。單元測試先期要編寫測試用例,是須要多耗費些時間,可是後面的調試、自測,均可以經過單元測試處理,不用手工一遍又一遍處理。實際上總時間被減小了。 ide
答:寫單元測試應該成爲開發人員的一種本能,開發自己就應該包含單元測試。 函數
結論: 工具
只進行手工測試,只是臨時性的單元測試,代碼測試覆蓋率要超過70%都很困難,未覆蓋的代碼可能遺留大量的細小的錯誤,這些錯誤還會互相影響,當bug暴露出來的時候難於調試,大幅度提升後期測試和維護成本。能夠說,進行充分的單元測試,是提升軟件質量,下降開發成本的必由之路。
要進行充分的單元測試,應專門編寫測試代碼,並與產品代碼隔離。比較簡單的辦法是爲產品工程創建對應的測試工程,爲每一個類創建對應的測試類,爲每一個函數(很簡單的除外)創建測試函數。
單元測試是由程序員本身來完成,最終受益的也是程序員本身。能夠這麼說,程序員有責任編寫功能代碼,同時也就有責任爲本身的代碼編寫單元測試。執行單元測試,就是爲了證實這段代碼的行爲和咱們指望的一致。
對於程序員來講,若是養成了對本身寫的代碼進行單元測試的習慣,不但能夠寫出高質量的代碼,並且還能提升編程水平。
3、單元測試工具。
在.Net平臺有三種單元測試工具,分別爲MS Test、NUnit、Xunit.Net。
1.MS Test爲微軟產品,集成在Visual Studio 2008+工具中。
2.NUnit爲.Net開源測試框架(採用C#開發),普遍用於.Net平臺的單元測試和迴歸測試中,官方網址(www.nunit.org)。
3.XUnit.Net爲NUnit的改進版。
(如下主要講解NUnit的使用,會了NUnit其餘2個測試工具也能快速熟悉)。
任何xUnit工具都使用斷言進行條件的判斷,NUnit天然也不例外,與其它的xUnit(如JUnit、phpUnit、pythonUnit)相比,因爲大量使用了Generic、Attribute等語言特徵,NUnit提供了更爲方面、靈活的測試方法,下面先介紹一下斷言。
NUnit一共有五個斷言類,分別是Assert、StringAssert、FileAssert、DirectoryAssert、CollectionAssert,它們都在NUnit.Framework命名空間,其中Assert是經常使用的,而另外四個斷言類,顧名思義,分別對應於字符串的斷言、文件的斷言、目錄的斷言、集合的斷言。理論上,僅Assert類就能夠完成全部條件的判斷,然而,若是合理的運用後面的四個斷言,將使代碼更加簡潔、美觀,也更加便於理解和維護。
4、NUnit的使用。
本處演示所使用的NUnit版本爲2.6.4,若要使用最新版能夠去官網下載。
首先建立一個類庫項目(也能夠是其餘項目),而後建立一個Test+類庫名稱的項目(也能夠是項目名稱+Test),用於表明是測試工程。以下圖:
Demonstration項目中含有一個計算功能類,對應的測試項目含有一個測試計算類,一個計算功能類中方法可能須要多個測試用例來完成檢測。以下展現出了2個類的代碼:
/// <summary> /// 用於演示的一個簡單計算功能 /// </summary> public class Calculate { /// <summary> /// 加法 /// </summary> public int Add(int a, int b) { return a + b; } /// <summary> /// 減法 /// </summary> public int Subtract(int a, int b) { return a - b; } /// <summary> /// 乘法 /// </summary> public int Multiply(short a, short b) { return a * b; } /// <summary> /// 除法 /// </summary> public int Quotient(int a, int b) { return a / b; } /// <summary> /// 開平方根 /// </summary> public double SquareRoot(int num) { return Math.Sqrt(num); } /// <summary> /// 四捨五入,取整 /// </summary> public int Round_Off(double num) { return (int)Math.Round(num); } /// <summary> /// 向上取整 /// </summary> public int UpwardTrunc(double num) { return (int)Math.Ceiling(num); } /// <summary> /// 平方 /// </summary> public int Square(short num) { throw new NotImplementedException(); } } [TestFixture(Description = "測試示例")] public class TestCalculate { private Calculate calculate; private StreamReader reader; private string[] sourceData = new string[] { @"..\..\..\Resource\score_1.csv" }; private short a, b; [TestFixtureSetUp] public void Initialize() { Console.WriteLine("初始化信息"); calculate = new Calculate(); } [TestFixtureTearDown] public void Dispose() { Console.WriteLine("釋放資源"); if (reader != null) { reader.Close(); } } [SetUp] public void SetUp() { a = 3; b = 2; } [TearDown] public void TearDown() { Console.WriteLine("我是清理者"); } [Test(Description = "加法")] [Category("優先級 1")] public void TestAdd() { Assert.AreEqual(5, calculate.Add(a, b)); } [Category("優先級 1")] [TestCase(1, 2), TestCase(2, 3)] public void TestSubtract(int a, int b) { Assert.AreEqual(a - b, calculate.Subtract(a, b)); } [Category("優先級 2")] [TestCase(1, 2, Result = 2), TestCase(2, 3, Result = 6)] public int TestMultiply(short a, short b) { return calculate.Multiply(a, b); } [Test] [Category("優先級 2")] [ExpectedException(typeof(DivideByZeroException))] public void TestQuotient() { calculate.Quotient(a, 0); } [Test] [Category("優先級 3")] public void TestSquareRoot() { Assert.Less(1, calculate.SquareRoot(a)); } [Test] [Category("優先級 3")] [Sequential] public void TestRound_Off([Values(3.4, 4.5, 4.6, 5.5)] double num, [Values(3, 5, 5, 6)] int result) { Assert.AreEqual(result, calculate.Round_Off(num)); } [Test] [Category("優先級 3")] public void TestUpwardTrunc([ValueSource("sourceData")] object fileName) { reader = new StreamReader((string)fileName); string content; while ((content = reader.ReadLine()) != null) { var nums = content.Split(',').Select(c => double.Parse(c)).ToArray(); Array.ForEach(nums, (num) => { int result = calculate.UpwardTrunc(num); Console.Write(result + "\n"); }); } } [Test] public void TestSquare() { Assert.Throws<NotImplementedException>(() => calculate.Square(b)); } [Test, Explicit] [Ignore] public void TestFactorial() { Assert.Fail("未能實現階乘功能"); } }
在粗略看了代碼後,下面就詳細說明相應的測試標記(屬性)的用法。
更多屬性標記與詳細說明,能夠查閱NUnit官網提供的說明文檔。一個方法的測試可能要寫不少個測試用例,這都是正常的,若是一個測試用例包含多個斷言,那些緊跟失敗斷言的斷言都不會執行,由於一般每一個測試方法最好只有一個斷言。
在運行單元測試時有3種方式分別爲:
以上的圖片展現了運行錯誤界面和運行輸出界面。在測試用例的節點中綠色'√'表明經過,黃色'√'表明忽略,紅色'×'表明失敗。
5、Nunit經常使用類和方法
一、Assert(斷言):若是斷言失敗,方法將沒有返回,而且報告一個錯誤。
1)、測試二個參數是否相等
Assert.AreEqual;
Assert.AreEqual;
2)、測試二個參數是否引用同一個對象
Assert.AreSame;
Assert.AreNotSame;
3)、測試一個對象是否被一個數組或列表所包含
Assert.Contains;
4)、測試一個對象是否大於另外一個對象
Assert.Greater;
5)、測試一個對象是否小於另外一個對象
Assert.Less;
6)、類型斷言:
Assert.IsInstanceOfType;
Assert.IsAssignableFrom;
7)、條件測試:
Assert.IsTrue;
Assert.IsFalse;
Assert.IsNull;
Assert.IsNotNull;
Assert.IsNaN;用來判斷指定的值是否爲數字。
Assert.IsEmpty;
Assert.IsNotEmpty;
Assert.IsEmpty;
Assert.IsNotEmpty;
8)、其餘斷言:
Assert.Fail;方法爲你提供了建立一個失敗測試的能力,這個失敗是基於其餘方法沒有封裝的測試。對於開發你本身的特定項目的斷言,它也頗有用。
Assert.Pass;強行讓測試經過
二、字符串斷言(StringAssert):提供了許多檢驗字符串值的有用的方法
StringAssert.Contains;
StringAssert.StartsWith;
StringAssert.EndsWith;
StringAssert.AreEqualIgnoringCase;
三、CollectionAssert類
CollectionAssert.AllItemsAreInstancesOfType;集合中的各項是不是某某類型的實例
CollectionAssert.AllItemsAreNotNull:集合中的各項均不爲空
CollectionAssert.AllItemsAreUnique;集合中的各項惟一
CollectionAssert.AreEqual;兩個集合相等
CollectionAssert.AreEquivalent;兩個集合至關
CollectionAssert.AreNotEqual;兩個集合不相等
CollectionAssert.AreNotEquivalent;兩個集合不至關
CollectionAssert.Contains;
CollectionAssert.DoesNotContain;集合中不包含某對象
CollectionAssert.IsSubsetOf:一個集合是另一個集合的子集
CollectionAssert.IsNotSubsetOf:一個集合不是另一個集合的子集
CollectionAssert.IsEmpty;集合爲空
CollectionAssert.IsNotEmpty;集合不爲空
CollectionAssert.IsOrdered;集合的各項已經排序
四、FileAssert
FileAssert.AreEqual;
FileAssert.AreNotEqual;
五、DirectoryAssert
DirectoryAssert.AreEqual;
DirectoryAssert.AreNotEqual;
DirectoryAssert.IsEmpty;
DirectoryAssert.IsNotEmpty;
DirectoryAssert.IsWithin;
DirectoryAssert.IsNotWithin;
6、NUnit集成到VS中的使用。
在使用NUnit-GUI處理運行測試用例,是否是感受比較麻煩,還要使用外部的NUnit應用程序,有沒有簡單點的最好可以跟VS開發工具緊密結合的方式來進行NUnit單元測試呢?答案是確定的,有2種方式。
1.咱們在VS中選擇工具菜單欄下的擴展和更新,選擇聯機並在搜索框中輸入NUnit。出現以下圖的信息,有2個版本的Nunit適配器,分別爲NUnit 3.x(最新版爲3.4.1)和NUnit 2.x(最新版爲2.6.4),都支持Visual Studio 2012+。若想在VS2010中集成,須要安裝NUnit 2.6.4安裝包(可在官網下載)與VS2010 NUnit整合插件(下載地址:
http://visualstudiogallery.msdn.microsoft.com/c8164c71-0836-4471-80ce-633383031099),下載安裝完畢就能在 VS2010 的視圖=>其餘窗口中看到 Visual Nunit(或使用快捷鍵Ctrl + F7),打開該視圖,將之拖到合適的位置。
下載安裝NUnit Test Adapter後關閉VS,重啓一下就行了,咱們打開類庫項目中的TestCalculate類,在右鍵彈出的菜單中點擊運行測試。運行結束後,會在左側的測試資源管理器當中顯示本次操做的結果。
2.經過ReSharper工具處理NUnit的單元測試,在VS2010+中安裝了ReSharper開發插件,ReSharper內中自帶支持NUnit與MS Test這2個單元測試工具,只要你的測試工程中引用了相應的單元測試類庫(如nunit.Framework.dll)、以及含有測試用例。經過鼠標右鍵或快捷鍵(Ctrl + T,R),就能夠運行單元測試,也能夠進行單元測試調試,ReSharper選項圖與運行效果以下圖。
7、後續
上面列出只能單元測試的基本使用,未能說明對Mock等其餘功能的使用,也沒有解釋對難以單元測試的代碼進行從新設計的說明,須要後期深刻了解才能列出相應的文檔說明。可以更好的使用單元測試才能更好的使用TDD(測試驅動開發)來開展項目,TDD測試驅動開發是測試先行(此測試是單元測試)、是極限編程的一個重要特色,它以不斷的測試推進代碼的開發,既簡化了代碼,同時也保證了軟件指令,另外一方面說編寫的測試用例將成爲重要文檔(能夠做爲SDK提供給開發者,測試即文檔)。
-----------------以上內容是根據博客園其餘博客的說明與Nunit官方文檔,以及本身測試使用,進行了整理說明。----------------------------