要使 TDD 按预期工作,您需要从被测单元的最简单方面开始。
你的第一个测试应该是:
[TestMethod]
public void GetEmployeesReturnsAList()
{
List<Employee> result = MyClass.GetEmployees();
Assert.IsNotNull(result);
}
并像这样实现:
public static List<Employee> GetEmployees()
{
return new List<Employee>();
}
现在,此测试应该在您添加代码以返回列表之前失败,并且从那时起,通过所有重构和将代码添加到被测单元,再也不会失败。
下一个测试将类似于:
// 这个测试是错误的!!它缺少可用员工的设置
我的目的是强调返回 1 名员工比返回预期的特定员工更简单。确保从更简单的情况开始。
// [TestMethod] // public void GetEmployeesReturnsOneEmployeeWhenThereIsOneEmployeeAvailable() //
{ // 列表结果 = MyClass.GetEmployees(); //
Assert.AreEqual(1, result.Count); // }
//
运行,测试失败。然后像这样重构:
public static List<Employee> GetEmployees()
{
Employee emp = new Employee();
List<Employee> empList = new List<Employee>();
empList.Add(emp);
return empList;
}
现在测试通过了。
下一个测试可能是:
[TestMethod]
public void GetEmployeesReturns_The_OneEmployeeWhenThereIsOneEmployeeAvailable()
{
// Arrange
Employee emp = new Employee();
// Add code here to insert emp into the source of the list
// This may be a mock.
// Action
List<Employee> result = MyClass.GetEmployees();
Assert.AreEqual(1, result.Count);
Assert.AreSame(emp, result[0]);
}
现在您添加并重新编写代码以使其通过而不破坏任何先前的单元测试。
冲洗并重复,添加尽可能少的代码以使测试通过而不会破坏任何其他测试。您将不得不多次重写代码,但结果是您获得了满足所有测试的最简单的代码。如果您的所有测试都完全代表了需求,那么您现在就拥有了一个功能齐全的单元。
TDD 的关键在于它可以防止不必要的复杂性,这种复杂性通常来自于使你的代码比它需要的更抽象,或者在你发现需要复杂性之前。当需要时,您总是可以事后使代码更加抽象。请注意,合法的需求甚至可能是满足代码分析工具的需求。
但是你可以自由地重构而不用担心,因为你有很好的测试覆盖率。
TDD 的另一件事是,您可能希望通过技术边缘案例测试(传递空参数并测试正确的异常)来扩充这些测试。但是,这不是必需的,这是 TDD 的另一个好处。
它强调了许多开发人员认为是“最佳实践”的事情,例如在不需要检查方法内部的方法参数时检查方法参数是否为 null,因为调用代码永远不应传入 null。如果调用代码确实传递了一个空值,那么问题就出在它而不是方法内部的代码上。
如果存在允许空值的真实代码路径,并且您的方法不期望空值,则调用代码必须自己执行空值检查并执行除调用您的方法之外的其他操作。