基于C#的单元测试(VS2015)
这次来联系怎么用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#单元测试。接下来,只需要对测试代码进行修改,测试跟多的样例,来对代码进行测试。