我们在很长一段时间内都会提出这个问题,而且经常会有很多争论。我们开发的系统使我们可以在不同的层(工人和控制器/演示者)上创建单元测试,模拟后续层,同时我们在演示者/控制器级别创建集成测试。
想象一下以下情景,
存储库
UserRepository
{
User GetByUserName(string username);
}
OrderRepository
{
List<Order> FindForUser(int userId);
}
工
UserOrderWorker
{
//constructor injected
UserRepository _userRepo;
OrderRepository _orderRepo;
IList<Order> FindOrders(string userName)
{
var user = _userRepo.GetByUserName(userName);
return _orderRepo.FindForUser(user.Id);
}
}
控制器
UserOrderController
{
View _view;
//constructor injected
UserOrderWorker _worker;
void Index()
{
_view.Orders = _worker. FindOrders(_view.UserName);
}
}
如果我们要通过模拟UserRepository和OrderRepository来为worker创建单元测试。我们还通过模拟视图和UserOrderController为UserOrderController创建单元测试。因此,为了解决这个问题,我们需要进行3次单元测试,并且还需要针对存储库进行2次集成测试。所以当天结束时总共进行了5次测试。
另一方面,如果我们要为此创建集成测试,我们只需要为UserOrderController创建一个。
仅针对使用集成测试的参数,
如果考虑到应用程序的实际使用情况,用户只能使用Controller,集成测试将覆盖最终用户的场景。
如果中间层已完成更改(假设只有通过将存储库方法FindForUser(int userId)
更改为FindForUser(int userId,List<Status> statuses))
来获取具有特定状态的订单,则需要进行的更改很少在集成测试中完成(在接受的状态之外添加额外的顺序),其中大多数测试需要在单元测试中更改(2个测试需要更改并且需要添加更多测试。
仅适用于使用单元测试的参数,
您可以更快地指出故障 - 但是,大多数情况下失败的原因是更改了方法签名(因为有人忘了配置模拟对象)
更好的文档 - 集成测试的文档应该足以识别客户需求,并且在我看来它已经足够好了。
考虑到以上所有因素,我不确定为什么我们实际上需要单元测试才能通过使用集成测试获得更好的结果。 (当然有人可能会说集成测试很昂贵,但我认为随着开发人员在业务需求变化时修改单元测试所花费的时间并不昂贵)
答案 0 :(得分:3)
单元测试和集成测试有不同的用途:
您的案例非常简单,单元测试可能不会为UserOrderController
提供一个体面的集成测试。但这只是因为你的类很小而且没有复杂的逻辑。
然而,当UserOrderWorker
变得更复杂时会发生什么?
_userRepo
因为没有找到用户而返回null,该怎么办?_userRepo
会引发异常,该怎么办?要使用集成测试测试此类方案,您必须对每个特定案例进行单独测试。乘以参与集成测试的组件数量。这通常太多了。单元测试不仅可以帮助您缓解此问题,而且由于其性质,还可以更精确地确定故障位置。
答案 1 :(得分:1)
你的例子太简单了,无法代表。该代码中没有复杂的逻辑。当你有很多ifs和循环时,它会改变。
假设您的申请中有n if
个陈述。这意味着您有2 ^ n个可能的不同执行流程。所以你可以用2个单元测试来测试每个if
,因此你有2n个单元测试。或者你只能进行集成测试。但是你需要2 ^ n次测试。
循环使问题更加复杂化。因此,当你没有逻辑(getters / setters / sequences)时,你可能根本不需要任何测试。但是当你有复杂的逻辑时,那么最好是单独测试那个复杂的东西
另一件事是集成测试的难度。例如,您要测试仅在年末运行的代码。在单元测试中准备环境(比如时间)更容易,然后在集成测试中,你不应该干扰系统内部的那么多