一段时间以来,我一直在阅读有关单元测试和功能测试的信息。
如果我编写详尽的功能测试,它们是否会反过来覆盖下面的单元,这反过来又使单元测试变得多余?
我们遵循敏捷,并在完成功能的“切片”后立即使用 WebDriver 编写功能测试,这通常是 2-4 周的 sprint 时间。
一段时间以来,我一直在阅读有关单元测试和功能测试的信息。
如果我编写详尽的功能测试,它们是否会反过来覆盖下面的单元,这反过来又使单元测试变得多余?
我们遵循敏捷,并在完成功能的“切片”后立即使用 WebDriver 编写功能测试,这通常是 2-4 周的 sprint 时间。
也许,但不一定。
功能测试可以被认为是“黑盒”测试,您正在查看特定功能(无论是单个方法、模块、系统等)并检查给定输入,您得到给定输出。
但是,如果功能测试失败,您只能说系统有故障;它不一定给你任何迹象表明系统的哪一部分是罪魁祸首。当然你会去诊断问题,但你不知道问题是什么。
例如
//assuming you have a UserService that amongst other things passes through to a UserRepository
var repo = new UserRepository();
var sut = new UserService(repo);
var user = sut.GetUserByID(1);
Assert.IsNotNull(user); //Suppose this fails.
在上面的例子中,你不知道是不是因为 UserService 的GetUserByID()
函数有问题——也许它没有调用repo.GetUserByID
,只是返回了 null,也许它确实得到了一个User
fromrepo
但意外地返回了一个未初始化的临时变量,等等——或者是因为依赖(repo
)本身是错误的。无论如何,您都必须调试问题。
另一方面,单元测试更像是“白盒”测试,您可以有效地摆脱系统的保护,并测试系统的行为方式以执行您希望它执行的操作。
例如(下面的代码可能无法编译,仅用于演示目的)
//setup a fake repo and fake the GetUserByID method to return a known instance:
var repo = new Mock<UserRepository>();
var user = new User{ID=1;}
repo.Setup(r=>r.GetUserByID(1).Returns(user);
var sut = new UserService(repo.Object);
var actual= sut.GetUserByID(1);
//now make sure we got back what we expected. If we didn't then UserService has a problem.
Assert.IsNotNull(user);
Assert.AreEqual(user,actual);
在这种情况下,您正在显式验证其sut.GetUserByID()
行为是否符合预期 - 它调用repo.GetUserByID()
并返回结果对象,而不破坏或更改它。
如果这个单元测试通过,但功能测试失败,你可以自信地说问题不在于UserService
,而在于UserRepository
类,甚至不用看代码。这可能会也可能不会节省您的时间,这实际上取决于您的功能单元的复杂程度。