【问题标题】:Testing an Edit view with MVCContrib Test Helper使用 MVCContrib 测试助手测试编辑视图
【发布时间】:2011-02-11 07:57:07
【问题描述】:

我正在使用 ASP.NET MVC 3、MVCContrib、NUnit 和 Rhino Mocks。我已经发布了这个但无法得到答案。人们更多地关注我的编码,以帮助我获得一个体面的答案以通过此测试。

我正在尝试测试我的编辑视图。我不确定如何为视图编写测试代码。它是 NewsEditViewData 类型的强类型视图。

当视图加载时,它会通过 ID 从新闻服务中获取新闻项目的数据。所以在我的测试中,我创建了一个新闻服务的存根:

var id = 1;
var news = new News();
newsServiceStub
   .Stub(x => x.FindById(id))
   .Return(news);

现在我需要将此新闻项映射到 NewsEditViewData。我有一个映射器(AutoMapper)为我做这件事,在我的测试中我做了以下事情:

newsMapperStub
   .Stub(x => x.Map(news, typeof(News), typeof(NewsEditViewData)))
   .Return(newsEditViewData);

我不确定到目前为止我是否走在正确的轨道上?

然后我有以下代码:

// Act
var actual = sut.Edit(id);

不要使用我的代码,我想要一个遵循最佳实践的答案。那么我需要在断言部分检查什么?我是否还需要检查记录是否已返回?我的想法是这样的:

actual
   .AssertViewRendered()
   .WithViewData<NewsEditViewData>()
   .ShouldBe(newsEditViewData);

这失败了。有人可以帮我重写这个测试,让它通过。我希望它检查所有需要检查的内容。

这是完整的测试:

[Test]
public void Edit_should_render_Edit_view()
{
   // Arrange
   var id = 1;
   var news = new News();
   var newsEditViewData = new NewsEditViewData();

   newsServiceStub
      .Stub(x => x.FindById(id))
      .Return(news);

   newsMapperStub
      .Stub(x => x.Map(news, typeof(News), typeof(NewsEditViewData)))
      .Return(newsEditViewData);

   // Act
   var actual = sut.Edit(id);

   // Assert
   actual
      .AssertViewRendered()
      .WithViewData<NewsEditViewData>()
      .ShouldBe(newsEditViewData);
}

2011-02-14 更新:

在我的 NewsController 中,我有以下内容:

private INewsService newsService;
private IMapper newsMapper;

public NewsController(INewsService newsService)
{
   Check.Argument.IsNotNull(newsService, "newsService");

   this.newsService = newsService;
   newsMapper = new NewsMapper();  // TODO: Use dependency injection
}

动作方法如下所示:

public ActionResult Edit(int id)
{
   Check.Argument.IsNotZeroOrNegative(id, "id");

   var news = newsService.FindById(id);
   var newsEditViewData = (NewsEditViewData)newsMapper.Map(news, typeof(News), typeof(NewsEditViewData));

   return View(newsEditViewData);
}

我在 NUnit 中遇到的错误是:

MyProject.Web.UnitTests.Controllers.NewsControllerTests.Edit_RenderView_EditView: MvcContrib.TestHelper.AssertionException :是 MyProject.Web.Common.ViewData.NewsEditViewData 但预期 MyProject.Web.Common.ViewData.NewsEditViewData

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-2 asp.net-mvc-3 mvccontrib mvccontrib-testhelper


    【解决方案1】:

    您没有显示任何控制器、存储库、模型。如果没有这些信息,这是一个几乎无法回答的问题。所以让我们开始猜测。你有一个模型和一个视图模型:

    public class News { }
    public class NewsEditViewData { }
    

    为了这篇文章的目的,我让它们没有任何属性。然后你可能有一个服务负责检索和保存你的模型(视图模型不应该作为你的服务层的输入/输出参数出现)。服务不应该知道视图模型:

    public interface INewsService
    {
        News FindById(int id);
        void CreateNews(News news);
    }
    

    那么你可能有一个映射器:

    public interface IMapper
    {
        object Map(object source, Type sourceType, Type destinationType);
    }
    

    最后我想你有一个控制器正在尝试测试:

    public class NewsController : Controller
    {
        private readonly INewsService _newsService;
        private readonly IMapper _newsMapper;
    
        public NewsController(INewsService newsService, IMapper newsMapper)
        {
            _newsService = newsService;
            _newsMapper = newsMapper;
        }
    
        public ActionResult Edit(int id)
        {
            // WARNING: Meaningless action ahead as it retrieves some
            // model from the service and passes this model to
            // the service back again for update. In the meantime
            // the model is converted to a view model using a mapper
            // and passed to the view. So totally meaningless in a real 
            // application but let's consider for the purpose of this demonstration
            var news = _newsService.FindById(id);
            _newsService.CreateNews(news);
            var newsEditViewData = (NewsEditViewData)_newsMapper.Map(news, typeof(News), typeof(NewsEditViewData));
            return View(newsEditViewData);
        }
    }
    

    好的,到此为止,您应该提供此信息。

    现在我可以开始回答你关于单元测试的问题了:

    [Test]
    public void Edit_should_fetch_news_model_from_service_given_an_id_parameter_create_news_and_pass_a_viewmodel_to_the_view()
    {
        // arrange
        // TODO : move this part in the initialization section 
        // of your unit test to avoid repeating it on each method
        var newsServiceStub = MockRepository.GenerateStub<INewsService>();
        var newsMapperStub = MockRepository.GenerateStub<IMapper>();
        var sut = new NewsController(newsServiceStub, newsMapperStub);
        new TestControllerBuilder().InitializeController(sut);
    
        var news = new News();
        var id = 123;
        var newsEditViewData = new NewsEditViewData();
        newsServiceStub
            .Stub(x => x.FindById(id))
            .Return(news);
    
        newsMapperStub
            .Stub(x => x.Map(news, typeof(News), typeof(NewsEditViewData)))
            .Return(newsEditViewData);
    
        // act
        var actual = sut.Edit(id);
    
        // assert
        actual
            .AssertViewRendered()
            .WithViewData<NewsEditViewData>()
            .ShouldBe(newsEditViewData);
        newsServiceStub.AssertWasCalled(x => x.CreateNews(news));
    }
    

    【讨论】:

    • 请查看更新后的帖子。你还有什么需要看的吗?我们的代码看起来一样,但测试仍然失败。
    • @Brendan Vogt,您的代码看起来不像我的。在 NewsController 的构造函数中,您对映射器进行了硬编码:newsMapper = new NewsMapper(); 而不是使用真正的依赖注入,这就是您的单元测试失败的原因。
    • 我看不出这里有没有硬编码的区别?当 NewsController 的构造函数启动时,它会创建新闻映射器的实例。使用依赖注入,您可以指定要提供的实例。你能解释一下吗?
    • @Brendan Vogt,您的单元测试失败,因为您试图在某些 newsMapperStub 上定义存根,但此变量从未在您的控制器中使用/传递,您在控制器中硬编码了真正的新闻映射器,这使得您的代码不可测试。因此,当在控制器内部调用 Map 方法时,真正的映射器会返回一些实例,而在您的单元测试中,您已经在此存根上定义了一个期望,即它将返回视图模型的一些预定义实例,并且您尝试断言视图已通过您的预定义视图模型实例,但事实并非如此。
    • 是的,我明白你的意思。谢谢。
    猜你喜欢
    • 2011-02-15
    • 2011-04-02
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多