序言:单元测试有许多优点,我觉得最明显的就是“防止回归”,回归缺陷是在对应用程序进行更改时引入的缺陷。 通常,测试人员不仅要测试新功能,还要测试预先存在的功能,以验证先前实现的功能是否仍按预期运行。使用单元测试,可在每次生成后,甚至在更改一行代码后重新运行整套测试。 让你确信新代码不会破坏现有功能。其次,当开发人员需要编写单元测试时,会自然的解耦代码,因为采用其他方法测试会更困难。
- 现在我们创建一个控制台应用程序,命名为ConsoleApp,然后在Program.cs文件中编写简单样例:
namespace HelloCSharp
{
class Program
{
static void Main(string[] args)
{
Driver driver = new Driver(new Car());
Console.WriteLine(driver.Drive());
}
}
public class Driver
{
private readonly IVehicle vehicle;
public Driver(IVehicle vehicle)
{
this.vehicle = vehicle;
}
public string Drive()
{
return vehicle.Run();
}
}
public interface IVehicle
{
string Run();
}
public class Car : IVehicle
{
public string Run()
{
return "Car is running";
}
}
public class Truck : IVehicle
{
public string Run()
{
return "Truck is running";
}
}
}
- 如上案例所示,当驾驶员(Driver)驾驶小汽车(Car)时会打印
Car is running,驾驶卡车(Truck)时会打印Truck is running,现在我们知道了驾驶员在驾驶小汽车或者卡车时应该打印什么才是正确的,现在我们开始编写单元测试案例。 - 首先右键单击解决方案 -> 添加 -> 新建项目,然后选择一个测试项目,这里我选择xUnit 测试项目,命名为ConsoleApp.Tests,这样命名能很清楚的让测试人员知道这个测试项目是测试ConsoleApp项目的。
- 右键单击ConsoleApp.Tests项目的依赖项 -> 添加项目引用 -> 勾选ConsoleApp项目 -> 确定:
- 编辑单元测试代码:
using HelloCSharp;
namespace ConsoleApp.Tests
{
public class UnitTest
{
[Fact]
public void TestDriver()
{
// 预期的结果
string successful = "Car is running";
Driver driver = new Driver(new Car());
string result = driver.Drive();
// 断言
Assert.Equal(successful, result);
}
}
}
- 我们预期正确的结果是
Car is running,然后判断返回的结果是否一致。在现实开发测试中并不会测试这么简单的功能,这里只是做个简单案例。 - 现在我们点击顶部的工具栏测试 -> 测试资源管理器 -> 出现如下界面:
- 现在可以右键单击TestDriver然后运行(也能直接右键cs文件然后点击运行测试),如果返回结果和预期一样就会显示成功:
- 当然我们也能直接右键ConsoleApp.Tests这个项目进行运行,这样的话就能测试该测试项目中所有的测试案例,也就是之前提到的“防止回归”,也就是修复了一个功能后导致之前的功能出现问题,这样就能直接测试出来。
- 现在我们做一次错误的案例演示:
// 原来的代码片段
public class Car : IVehicle
{
public string Run()
{
return "Car is running";
}
}
// 改成"Tank is running",这明显是和测试案例中的预期不符合的。
public class Car : IVehicle
{
public string Run()
{
return "Tank is running";
}
}
- 测试资源管理器会清楚的提示哪里出现了问题:
结语:在实际工作中,我们测试会比这复杂许多,需要在NuGet中下载Moq,通过mock来进行更加高效的测试。
本文作者:小赞(clozer)
邮箱:clozer@foxmail.com
未经授权严禁转载及使用