【问题标题】:Moq out parameters起订量参数
【发布时间】:2016-06-28 15:21:57
【问题描述】:

我对使用 Moq 和 Nunit 进行单元测试还很陌生,我遇到了一个场景问题。我想要的是让我的模拟有一个输出参数,然后我的被测系统将使用这些参数来决定采取什么行动。

我的测试系统是一个 MVC API 控制器,特别是我正在尝试测试 POST 方法。我想在验证失败时返回对象的错误消息。

这是控制器的方法代码:

        public IHttpActionResult Post(Candidate candidate)
    {
        try
        {
            if(candidate==null)
                return BadRequest();

            IEnumerable<string> errors;
            _candidateManager.InsertCandidate(candidate, out errors);

            if (errors!=null && errors.Any())
                return BadRequest(CreateErrorMessage("Invalid candidate: ", errors));

            return CreatedAtRoute("DefaultApi", new {id = candidate.CandidateId}, candidate);

        }
        catch (Exception)
        {
            return InternalServerError();
        }
    }

这是我的单元测试代码:

        [Test]
    [Category("CandidateManagerController Unit Tests")]
    public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
    {
        IEnumerable<string> errors = new List<string>() {"error1", "error2"};

        var mockManager = new Mock<ICandidateManager>();
        mockManager.Setup(x => x.InsertCandidate(new Candidate(), out errors)).Callback(()=>GetErrors(errors));

        var sut = new CandidateManagerController(mockManager.Object);

        var actionResult = sut.Post(new Candidate());

        Assert.IsInstanceOf<BadRequestResult>(actionResult);

    }

我期望的是,当 _candidateManager.InsertCandidate() 运行时,错误变量会被填充。但是发生的情况是,当您单步执行控制器代码时,_candidateManager.InsertCandidate() 方法运行后错误为空。

如果有人知道我做错了什么,或者如果使用 Moq 无法实现我想要做的事情,请告诉我。

谢谢

【问题讨论】:

  • 你必须设置错误。 GetErrors的实现是什么?

标签: unit-testing nunit moq


【解决方案1】:

你想做的都是可能的。如果您查看https://github.com/Moq/moq4/wiki/Quickstart 的快速入门文档,其中有一个部分显示了您如何使用 out 参数对方法进行设置。我对您的代码进行了两次更正,并且可以正常工作。

  1. 在模拟设置和练习 sut 时,您必须使用相同的候选实例。否则,Moq 会认为这两个对象不同,您的测试设置将变得毫无用处。
  2. 您不必使用回调来设置模拟的 CandidateManager 返回的错误。 以下是您的测试方法以及我的更改。

    [Test]
    [Category("CandidateManagerController Unit Tests")]
    public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
     {
    IEnumerable<string> errors = new List<string>() {"error1", "error2"};
    
    //instance to be used for both setup and test later
    var candidate = new Candidate(); 
    
    var mockManager = new Mock<ICandidateManager>();
    
    //removed Callback
    mockManager.Setup(x => x.InsertCandidate(candidate, out errors));
    
    var sut = new CandidateManagerController(mockManager.Object);
    
    var actionResult = sut.Post(candidate);
    
    Assert.IsInstanceOf<BadRequestResult>(actionResult);
    
     }
    

【讨论】:

    【解决方案2】:

    您必须确保在调用 SUT 时使用传递给 out 参数的同一实例,否则调用将失败。

    在您的示例中,被测方法将一个空实例传递给模拟方法,从而否定了测试的设置。

    但是,如果您无法为 out 提供相同的实例,那么看起来您将无法成功通过模拟。查看Quick Start for Moq 以了解它的功能。

    【讨论】:

    • 您好,感谢您到目前为止的回复。抱歉,设置模拟的代码应该是 var mockManager = new Mock&lt;ICandidateManager&gt;(); mockManager.Setup(x =&gt; x.InsertCandidate(new Candidate(), out errors)); 当我在 actionResult 行上放一个断点时,我可以看到错误包含我期望的两个项目,但是当你进入控制器代码时,错误 IEnumerable 不是在调用方法时设置。阅读最小起订量快速入门,看起来我已经设置了相同的。我不确定的是我想做的事情是否真的可行。
    猜你喜欢
    • 2015-10-10
    • 2015-02-02
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    • 2013-12-28
    相关资源
    最近更新 更多