单元测试
对单元测试的描述大家可自行百度,后期时间满足的情况下,我在对单元测试进行一个单独的专题进行说明,这一节主要就是介绍一下简单的用法。
首先我们创建一个控制台项目,添加引用 system.windows.forms,为了下面的类做铺垫
第一步 首先创建控制台项目,添加类 WC.cs
本类主要是对文件文字总字数的一个帮助类,接下去的单位测试也是基于它进行,首先附上代码
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;
}
}
第二步 添加单元测试
LZ使用的是VS2017,通过右击可以得到如下截图所示内容,点击创建单元测试,保持默认选项不变,点击确认
单击 “创建单元测试” 后,会出项如下对话框。不用修改,保持默认选项就可以。
如果菜单没有显示 测试,可以参照这篇博客进行设置。http://www.bubuko.com/infodetail-1370830.html
添加后的结果如下
image.png image.png进行单元测试
接下来,我们对测试代码进行修改,在我们进行单元测试时,某种程度上就是将我们人工给出的程序运行结果与程序实际输出结果进行比较,所以单元测试的过程一般分为 3 步:
给出我们期望的结果 expected
执行需测试代码,返回结果 actual
比较 actual 和 expected
下面以 WC 程序执行 -c 参数对 123.txt 文件进行统计的功能为例进行测试,我们将测试代码修改如下,其中 AreEqual 方法用于对期望值与实际值进行比较。
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ConsoleApp2;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2.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" 文件内容:
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
}
}
}
我们预期的测试结果是此文件有124个字符,所以会输出 “字符数:124” ;我们来看看测试的结果,点击右键,选择 “运行测试” ,选项测试通过了。
image.png image.png如果我们预期此文件有130个字符,单元测试就会报错,并且给出比对结果
image.png
编写测试方法
单元测试的基本方法是调用被测代码的函数,输入函数的参数值,获取返回结果,然后与预期测试结果进行比较,如果相等则认为测试通过,否则认为测试不通过。
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