這次來聯系怎么用VS2015來進行C#代碼的單元測試管理,首先,正好上次寫了一個C#的WordCount程序,就用它來進行單元測試聯系吧。
首先,根據VS2015的提示,僅支持在共有類或共有方法中支持創建單元測試。所以,如果我們要測試私有或是保護的類和方法,是要先將他們暫時設定成公有類型。
在VS2015中創建單元測試非常簡單,只要在我們想測試的地方點擊右鍵,就會出現 “創建單元測試” 選項。
如果發現菜單沒有顯示,可以參照這篇博客進行設置。http://www.bubuko.com/infodetail-1370830.html
單擊 “創建單元測試” 后,會出項如下對話框。不用修改,保持默認選項就可以。
點擊“確定”,耐心等待一會。
創建完成后,會出項一個名為 “WCTests” 的文件,文件初始代碼為
1 using Microsoft.VisualStudio.TestTools.UnitTesting; 2 using wc; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace wc.Tests 10 { 11 [TestClass()] 12 public class WCTests 13 { 14 [TestMethod()] 15 public void WCTest() 16 { 17 Assert.Fail(); 18 } 19 20 [TestMethod()] 21 public void OperatorTest() 22 { 23 Assert.Fail(); 24 } 25 } 26 }
在進行單元測試時,主要使用的是 Assert 類,他的用發有很多,詳細用法可以參照 https://msdn.microsoft.com/zh-cn/library/microsoft.visualstudio.testtools.unittesting.assert.aspx
下面給出我們要進行測試的代碼,為了方便測試,我們對其中代碼輸出的部分進行了修改,將原先控制台輸出的部分改成了函數返回。
1 public class WC 2 { 3 private string sFilename; // 文件名 4 private string[] sParameter; // 參數數組 5 private int iCharcount; // 字符數 6 private int iWordcount; // 單詞數 7 private int iLinecount; // 行 數 8 private int iNullLinecount; // 空行數 9 private int iCodeLinecount; // 代碼行數 10 private int iNoteLinecount; // 注釋行數 11 12 // 初始化 13 public WC() 14 { 15 this.iCharcount = 0; 16 this.iWordcount = 0; 17 this.iLinecount = 0; 18 this.iNullLinecount = 0; 19 this.iCodeLinecount = 0; 20 this.iNoteLinecount = 0; 21 } 22 // 控制信息 23 public string Operator(string[] sParameter, string sFilename) 24 { 25 this.sParameter = sParameter; 26 this.sFilename = sFilename; 27 28 string retrun_str = ""; 29 30 foreach (string s in sParameter) 31 { 32 if(s == "-x") 33 { 34 string resultFile = ""; 35 OpenFileDialog fd = new OpenFileDialog(); 36 fd.InitialDirectory = "D:\\Patch"; 37 fd.Filter = "All files (*.*)|*.*|txt files (*.txt)|*.txt"; 38 fd.FilterIndex = 2; 39 fd.RestoreDirectory = true; 40 if (fd.ShowDialog() == DialogResult.OK) 41 { 42 resultFile = fd.FileName; 43 //Console.WriteLine("文件名:{0}", resultFile); 44 SuperCount(resultFile); 45 BaseCount(resultFile); 46 retrun_str = DisplayAll(); 47 } 48 break; 49 } 50 // 遍歷文件 51 else if (s == "-s") 52 { 53 try 54 { 55 string[] arrPaths = sFilename.Split('\\'); 56 int pathsLength = arrPaths.Length; 57 string path = ""; 58 59 // 獲取輸入路徑 60 for (int i = 0; i < pathsLength - 1; i++) 61 { 62 arrPaths[i] = arrPaths[i] + '\\'; 63 64 path += arrPaths[i]; 65 } 66 67 // 獲取通配符 68 string filename = arrPaths[pathsLength - 1]; 69 70 // 獲取符合條件的文件名 71 string[] files = Directory.GetFiles(path, filename); 72 73 foreach (string file in files) 74 { 75 //Console.WriteLine("文件名:{0}", file); 76 SuperCount(file); 77 BaseCount(file); 78 retrun_str = Display(); 79 } 80 break; 81 } 82 catch (IOException ex) 83 { 84 //Console.WriteLine(ex.Message); 85 return ""; 86 } 87 } 88 // 高級選項 89 else if (s == "-a") 90 { 91 //Console.WriteLine("文件名:{0}", sFilename); 92 SuperCount(sFilename); 93 BaseCount(sFilename); 94 retrun_str = Display(); 95 break; 96 } 97 // 基本功能 98 else if (s == "-c" || s == "-w" || s == "-l") 99 { 100 //Console.WriteLine("文件名:{0}", sFilename); 101 BaseCount(sFilename); 102 retrun_str = Display(); 103 break; 104 } 105 else 106 { 107 //Console.WriteLine("參數 {0} 不存在", s); 108 break; 109 } 110 } 111 Console.WriteLine("{0}", retrun_str); 112 return retrun_str; 113 } 114 115 // 統計基本信息:字符數 單詞數 行數 116 private void BaseCount(string filename) 117 { 118 try 119 { 120 // 打開文件 121 FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); 122 StreamReader sr = new StreamReader(file); 123 int nChar; 124 int charcount = 0; 125 int wordcount = 0; 126 int linecount = 0; 127 //定義一個字符數組 128 char[] symbol = { ' ', ',', '.', '?', '!', ':', ';', '\'', '\"', '\t', '{', '}', '(', ')', '+' ,'-', 129 '*', '='}; 130 while ((nChar = sr.Read()) != -1) 131 { 132 charcount++; // 統計字符數 133 134 foreach (char c in symbol) 135 { 136 if(nChar == (int)c) 137 { 138 wordcount++; // 統計單詞數 139 } 140 } 141 if (nChar == '\n') 142 { 143 linecount++; // 統計行數 144 } 145 } 146 iCharcount = charcount; 147 iWordcount = wordcount + 1; 148 iLinecount = linecount + 1; 149 sr.Close(); 150 } 151 catch (IOException ex) 152 { 153 Console.WriteLine(ex.Message); 154 return; 155 } 156 } 157 158 // 統計高級信息:空行數 代碼行數 注釋行數 159 private void SuperCount(string filename) 160 { 161 try 162 { 163 // 打開文件 164 FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); 165 StreamReader sr = new StreamReader(file); 166 String line; 167 int nulllinecount = 0; 168 int codelinecount = 0; 169 int notelinecount = 0; 170 while ((line = sr.ReadLine()) != null) 171 { 172 line = line.Trim(' '); 173 line = line.Trim('\t'); 174 // 空行 175 if (line == "" || line.Length <= 1) 176 { 177 nulllinecount++; 178 } 179 // 注釋行 180 else if(line.Substring(0, 2) == "//" || line.Substring(1, 2) == "//") 181 { 182 notelinecount++; 183 } 184 // 代碼行 185 else 186 { 187 codelinecount++; 188 } 189 } 190 iNullLinecount = nulllinecount; 191 iCodeLinecount = codelinecount; 192 iNoteLinecount = notelinecount; 193 sr.Close(); 194 } 195 catch (IOException ex) 196 { 197 Console.WriteLine(ex.Message); 198 return; 199 } 200 } 201 // 打印信息 202 private string Display() 203 { 204 string return_str = ""; 205 206 foreach (string s in sParameter) 207 { 208 if (s == "-c") 209 { 210 //Console.WriteLine("字 符 數:{0}", iCharcount); 211 return_str += "字符數:"+iCharcount.ToString(); 212 } 213 else if (s == "-w") 214 { 215 //Console.WriteLine("單 詞 數:{0}", iWordcount); 216 return_str += "單詞數:" + iWordcount.ToString(); 217 } 218 else if (s == "-l") 219 { 220 //Console.WriteLine("總 行 數:{0}", iLinecount); 221 return_str += "總行數:" + iLinecount.ToString(); 222 } 223 else if(s == "-a") 224 { 225 //Console.WriteLine("空 行 數:{0}", iNullLinecount); 226 //Console.WriteLine("代碼行數:{0}", iCodeLinecount); 227 //Console.WriteLine("注釋行數:{0}", iNoteLinecount); 228 return_str += "空行數:" + iNullLinecount.ToString(); 229 return_str += "代碼行數:" + iCodeLinecount.ToString(); 230 return_str += "注釋行數:" + iNoteLinecount.ToString(); 231 } 232 } 233 //Console.WriteLine(); 234 return return_str; 235 } 236 private string DisplayAll() 237 { 238 string return_str = ""; 239 foreach (string s in sParameter) 240 { 241 //Console.WriteLine("字 符 數:{0}", iCharcount); 242 //Console.WriteLine("單 詞 數:{0}", iWordcount); 243 //Console.WriteLine("總 行 數:{0}", iLinecount); 244 //Console.WriteLine("空 行 數:{0}", iNullLinecount); 245 //Console.WriteLine("代碼行數:{0}", iCodeLinecount); 246 //Console.WriteLine("注釋行數:{0}", iNoteLinecount); 247 return_str += "字符數:" + iCharcount.ToString(); 248 return_str += "單詞數:" + iWordcount.ToString(); 249 return_str += "總行數:" + iLinecount.ToString(); 250 return_str += "空行數:" + iNullLinecount.ToString(); 251 return_str += "代碼行數:" + iCodeLinecount.ToString(); 252 return_str += "注釋行數:" + iNoteLinecount.ToString(); 253 } 254 //Console.WriteLine(); 255 return return_str; 256 } 257 }
接下來,我們對測試代碼進行修改,在我們進行單元測試時,某種程度上就是將我們人工給出的程序運行結果與程序實際輸出結果進行比較,所以單元測試的過程一般分為 3 步:
- 給出我們期望的結果 expected
- 執行需測試代碼,返回結果 actual
- 比較 actual 和 expected
下面以 WC 程序執行 -c 參數對 123.txt 文件進行統計的功能為例進行測試,我們將測試代碼修改如下,其中 AreEqual 方法用於對期望值與實際值進行比較。
1 using Microsoft.VisualStudio.TestTools.UnitTesting; 2 using wc; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace wc.Tests 10 { 11 [TestClass()] 12 public class WCTests 13 { 14 [TestMethod()] 15 public void WCTest() 16 { 17 // arrange 18 string expected = "字符數:138"; 19 WC testwc = new WC(); 20 21 // act 22 string[] opar = new string[1]; 23 opar[0] = "-c"; 24 25 string actual = testwc.Operator(opar, @"D:\學習\我的工程\軟件工程\wc\wc\wc\123.txt"); 26 27 // assert 28 Assert.AreEqual(expected, actual); 29 } 30 } 31 }
"123.txt" 文件為
我們預期的測試結果是此文件有138個字符,所以會輸出 “字符數:138” ;我們來看看測試的結果,點擊右鍵,選擇 “運行測試” ,選項測試通過了。
我們現在給出一個錯誤的預期來看看結果會如何,我們現在認為 "123.txt" 文件有50個字符。
系統會提示出錯,並且給出實際值與期望值分別是多少。至此,我們已經完成了簡單的C#單元測試。接下來,只需要對測試代碼進行修改,測試跟多的樣例,來對代碼進行測試。