根據VS2015的提示,僅支持在共有類或共有方法中支持創建單元測試。所以,如果我們要測試私有或是保護的類和方法,是要先將他們暫時設定成公有類型。
在VS2015中創建單元測試,只要在我們想測試的地方點擊右鍵,就會出現 “創建單元測試” 選項。
如果菜單沒有顯示 測試,可以參照這篇博客進行設置。http://www.bubuko.com/infodetail-1370830.html
單擊 “創建單元測試” 后,會出項如下對話框。不用修改,保持默認選項就可以。
點擊“確定”
創建完成后,會出項一個名為 “WCTests” 的文件,代碼:
using Microsoft.VisualStudio.TestTools.UnitTesting; using wc; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wc.Tests { [TestClass()] public class WCTests { [TestMethod()] public void WCTest() { Assert.Fail(); } [TestMethod()] public void OperatorTest() { Assert.Fail(); } } }
在進行單元測試時,主要使用的是 Assert 類,他的用發有很多,詳細用法可以參照 https://msdn.microsoft.com/zh-cn/library/microsoft.visualstudio.testtools.unittesting.assert.aspx
下面給出我們要進行測試的代碼,為了方便測試,我們對其中代碼輸出的部分進行了修改,將原先控制台輸出的部分改成了函數返回。
public class WC { private string sFilename; // 文件名 private string[] sParameter; // 參數數組 private int iCharcount; // 字符數 private int iWordcount; // 單詞數 private int iLinecount; // 行 數 private int iNullLinecount; // 空行數 private int iCodeLinecount; // 代碼行數 private int iNoteLinecount; // 注釋行數 // 初始化 public WC() { this.iCharcount = 0; this.iWordcount = 0; this.iLinecount = 0; this.iNullLinecount = 0; this.iCodeLinecount = 0; this.iNoteLinecount = 0; } // 控制信息 public string Operator(string[] sParameter, string sFilename) { this.sParameter = sParameter; this.sFilename = sFilename; string retrun_str = ""; foreach (string s in sParameter) { if(s == "-x") { string resultFile = ""; OpenFileDialog fd = new OpenFileDialog(); fd.InitialDirectory = "D:\\Patch"; fd.Filter = "All files (*.*)|*.*|txt files (*.txt)|*.txt"; fd.FilterIndex = 2; fd.RestoreDirectory = true; if (fd.ShowDialog() == DialogResult.OK) { resultFile = fd.FileName; //Console.WriteLine("文件名:{0}", resultFile); SuperCount(resultFile); BaseCount(resultFile); retrun_str = DisplayAll(); } break; } // 遍歷文件 else if (s == "-s") { try { string[] arrPaths = sFilename.Split('\\'); int pathsLength = arrPaths.Length; string path = ""; // 獲取輸入路徑 for (int i = 0; i < pathsLength - 1; i++) { arrPaths[i] = arrPaths[i] + '\\'; path += arrPaths[i]; } // 獲取通配符 string filename = arrPaths[pathsLength - 1]; // 獲取符合條件的文件名 string[] files = Directory.GetFiles(path, filename); foreach (string file in files) { //Console.WriteLine("文件名:{0}", file); SuperCount(file); BaseCount(file); retrun_str = Display(); } break; } catch (IOException ex) { //Console.WriteLine(ex.Message); return ""; } } // 高級選項 else if (s == "-a") { //Console.WriteLine("文件名:{0}", sFilename); SuperCount(sFilename); BaseCount(sFilename); retrun_str = Display(); break; } // 基本功能 else if (s == "-c" || s == "-w" || s == "-l") { //Console.WriteLine("文件名:{0}", sFilename); BaseCount(sFilename); retrun_str = Display(); break; } else { //Console.WriteLine("參數 {0} 不存在", s); break; } } Console.WriteLine("{0}", retrun_str); return retrun_str; } // 統計基本信息:字符數 單詞數 行數 private void BaseCount(string filename) { try { // 打開文件 FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); StreamReader sr = new StreamReader(file); int nChar; int charcount = 0; int wordcount = 0; int linecount = 0; //定義一個字符數組 char[] symbol = { ' ', ',', '.', '?', '!', ':', ';', '\'', '\"', '\t', '{', '}', '(', ')', '+' ,'-', '*', '='}; while ((nChar = sr.Read()) != -1) { charcount++; // 統計字符數 foreach (char c in symbol) { if(nChar == (int)c) { wordcount++; // 統計單詞數 } } if (nChar == '\n') { linecount++; // 統計行數 } } iCharcount = charcount; iWordcount = wordcount + 1; iLinecount = linecount + 1; sr.Close(); } catch (IOException ex) { Console.WriteLine(ex.Message); return; } } // 統計高級信息:空行數 代碼行數 注釋行數 private void SuperCount(string filename) { try { // 打開文件 FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); StreamReader sr = new StreamReader(file); String line; int nulllinecount = 0; int codelinecount = 0; int notelinecount = 0; while ((line = sr.ReadLine()) != null) { line = line.Trim(' '); line = line.Trim('\t'); // 空行 if (line == "" || line.Length <= 1) { nulllinecount++; } // 注釋行 else if(line.Substring(0, 2) == "//" || line.Substring(1, 2) == "//") { notelinecount++; } // 代碼行 else { codelinecount++; } } iNullLinecount = nulllinecount; iCodeLinecount = codelinecount; iNoteLinecount = notelinecount; sr.Close(); } catch (IOException ex) { Console.WriteLine(ex.Message); return; } } // 打印信息 private string Display() { string return_str = ""; foreach (string s in sParameter) { if (s == "-c") { //Console.WriteLine("字 符 數:{0}", iCharcount); return_str += "字符數:"+iCharcount.ToString(); } else if (s == "-w") { //Console.WriteLine("單 詞 數:{0}", iWordcount); return_str += "單詞數:" + iWordcount.ToString(); } else if (s == "-l") { //Console.WriteLine("總 行 數:{0}", iLinecount); return_str += "總行數:" + iLinecount.ToString(); } else if(s == "-a") { //Console.WriteLine("空 行 數:{0}", iNullLinecount); //Console.WriteLine("代碼行數:{0}", iCodeLinecount); //Console.WriteLine("注釋行數:{0}", iNoteLinecount); return_str += "空行數:" + iNullLinecount.ToString(); return_str += "代碼行數:" + iCodeLinecount.ToString(); return_str += "注釋行數:" + iNoteLinecount.ToString(); } } //Console.WriteLine(); return return_str; } private string DisplayAll() { string return_str = ""; foreach (string s in sParameter) { //Console.WriteLine("字 符 數:{0}", iCharcount); //Console.WriteLine("單 詞 數:{0}", iWordcount); //Console.WriteLine("總 行 數:{0}", iLinecount); //Console.WriteLine("空 行 數:{0}", iNullLinecount); //Console.WriteLine("代碼行數:{0}", iCodeLinecount); //Console.WriteLine("注釋行數:{0}", iNoteLinecount); return_str += "字符數:" + iCharcount.ToString(); return_str += "單詞數:" + iWordcount.ToString(); return_str += "總行數:" + iLinecount.ToString(); return_str += "空行數:" + iNullLinecount.ToString(); return_str += "代碼行數:" + iCodeLinecount.ToString(); return_str += "注釋行數:" + iNoteLinecount.ToString(); } //Console.WriteLine(); return return_str; } }
接下來,我們對測試代碼進行修改,在我們進行單元測試時,某種程度上就是將我們人工給出的程序運行結果與程序實際輸出結果進行比較,所以單元測試的過程一般分為 3 步:
- 給出我們期望的結果 expected
- 執行需測試代碼,返回結果 actual
- 比較 actual 和 expected
下面以 WC 程序執行 -c 參數對 123.txt 文件進行統計的功能為例進行測試,我們將測試代碼修改如下,其中 AreEqual 方法用於對期望值與實際值進行比較。
using Microsoft.VisualStudio.TestTools.UnitTesting; using wc; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wc.Tests { [TestClass()] public class WCTests { [TestMethod()] public void WCTest() { // arrange string expected = "字符數:138"; WC testwc = new WC(); // act string[] opar = new string[1]; opar[0] = "-c"; string actual = testwc.Operator(opar, @"D:\學習\我的工程\軟件工程\wc\wc\wc\123.txt"); // assert Assert.AreEqual(expected, actual); } } }
"123.txt" 文件:
我們預期的測試結果是此文件有138個字符,所以會輸出 “字符數:138” ;我們來看看測試的結果,點擊右鍵,選擇 “運行測試” ,選項測試通過了。
我們現在給出一個錯誤的預期來看看結果會如何,我們現在認為 "123.txt" 文件有50個字符。
系統會提示出錯,並且給出實際值與期望值分別是多少。
編寫測試方法
單元測試的基本方法是調用被測代碼的函數,輸入函數的參數值,獲取返回結果,然后與預期測試結果進行比較,如果相等則認為測試通過,否則認為測試不通過。
1、Assert類的使用
Assert.Inconclusive() 表示一個未驗證的測試;
Assert.AreEqual() 測試指定的值是否相等,如果相等,則測試通過;
AreSame() 用於驗證指定的兩個對象變量是指向相同的對象,否則認為是錯誤
AreNotSame() 用於驗證指定的兩個對象變量是指向不同的對象,否則認為是錯誤
Assert.IsTrue() 測試指定的條件是否為True,如果為True,則測試通過;
Assert.IsFalse() 測試指定的條件是否為False,如果為False,則測試通過;
Assert.IsNull() 測試指定的對象是否為空引用,如果為空,則測試通過;
Assert.IsNotNull() 測試指定的對象是否為非空,如果不為空,則測試通過;
2、CollectionAssert類的使用
用於驗證對象集合是否滿足條件
StringAssert類的使用
用於比較字符串。
StringAssert.Contains
StringAssert.Matches
StringAssert.tartWith