基于C#的单元测试(VS2015)

Posted Li Baoquan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于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#单元测试。接下来,只需要对测试代码进行修改,测试跟多的样例,来对代码进行测试。

以上是关于基于C#的单元测试(VS2015)的主要内容,如果未能解决你的问题,请参考以下文章

VS2015 C#的单元测试

第二次作业—单元测试的简单实践

SonarQube 5.6 中的 C# 单元测试结果

防止 VS C# 单元测试因异常而中断

VS2015 单元测试

VS2015做单元测试