我有一个叫做NewsController的控制器,这个控制器的构造函数接收一个INewsService作为参数。
我在这个控制器中有一个名为GetAllNews()的方法,它返回一个JSON结果,用于填充YUI数据表。我想编写一个单元测试,检查新闻服务的FindAll方法是否被调用以返回所有新闻项。我该怎么做?我现在拥有的是:
public JsonResult GetAllNews()
{
var items = newsService.FindAll();
var jsonResult = Json(items);
return jsonResult;
}
我对控制器的单元测试如下:
public NewsControllerTest()
{
newsServiceStub = MockRepository.GenerateStub<INewsService>();
newsController = new NewsController(newsServiceStub);
}
[Test]
public void GetAllNews_should_use_news_service()
{
// Arrange
List<News> newsList = new List<News>();
newsServiceStub.Stub(s => s.FindAll()).Return(newsList);
// Act
var actual = newsController.GetAllNews();
// Assert
newsServiceStub.VerifyAllExpectations();
}
测试通过上面的代码。但是,如果我要将GetAllNews()更改为如下所示,那么它也会通过。不应该失败吗?我想测试的是GetAllNews()是否使用新闻服务:
public JsonResult GetAllNews()
{
return null;
}
答案 0 :(得分:3)
如果你能逃脱它,不要单元测试是否调用了特定的方法。单元测试的重点是测试行为,而不是实现。测试FindAll
的测试是测试实现。如果您更改实现但行为没有改变,这会导致脆弱的测试中断。客户并不关心你如何获得所有新闻,他们只是希望你得到所有新闻。
所以
public void GetAllNews_should_use_news_service()
应该是
public void GetAllNews_should_get_all_the_news
我会留下编码的详细信息。
答案 1 :(得分:1)
正如其他人所指出的,从长远来看,对特定方法调用的测试可能会很脆弱。
然而,从Rhino.Mocks的角度来看,如果你想检查期望,你应该使用Mock而不是Stub。将您GenerateStub
更改为GenerateMock
,将.Stub()
来电更改为.Expect()
来电。那应该可以解决你的考试。
答案 2 :(得分:1)
你可能会发现Martin Fowler的这篇文章对于模拟和存根之间的差异感兴趣。
http://www.martinfowler.com/articles/mocksArentStubs.html
Fowler指出存根用于状态验证,而模拟用于行为验证。