Moq-用测试执行期间更改的参数验证呼叫 [英] Moq - verifying a call with parameter that is changed during the execution of the test

查看:84
本文介绍了Moq-用测试执行期间更改的参数验证呼叫的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出以下代码

public class Entity
{
    public string Name { get; set; }
    public string Status { get; set; }
}

public interface IRepository
{
    void InsertEntity(Entity entity);
    void UpdateEntity(Entity entity);
}

public class Processor
{
    private IRepository _repository;

    public Processor(IRepository repository)
    {
        _repository = repository;
    }

    public void Execute(string name)
    {
        var entity = new Entity() { Name = name, Status = "Initialized" };
        _repository.InsertEntity(entity);
        // do other things with the entity
        entity.Status = "Processed";
        _repository.UpdateEntity(entity);
    }
}

我可以编写一个单元测试,以验证是否在Execute方法内部调用了存储库,并使用InsertEntity方法保存了实体的值.换句话说,我想确保在调用InsertEntity时,实体的Status属性的值是"Initialized".所以我的单元测试将是这样的:

I could write a unit test to verify if the repository is called inside the Execute method, saving the value of the entity with the method InsertEntity. In other words, I'd like to make sure that when InsertEntity is called, the value of Status property of the entity is "Initialized". So my unit test would be like this:

[TestMethod]
public void ShouldSaveEntityWithStatusInitialized()
{
    var mock = new Mock<IRepository>();
    var processor = new Processor(mock.Object);
    processor.Execute("test");
    mock.Verify(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Initialized")), Times.Once()); // fail
}

但是,即使使用Status ="Initialized"调用InsertEntity方法,此代码也会失败(我已经调试了该方法).我认为这是因为实体对象在Execute方法的执行过程中发生了更改(最后,Status属性更改为"Processed",并且Moq针对更改后的对象验证了调用).实际上,另一个单元测试效果很好.

However, this code fails even calling InsertEntity method with Status = "Initialized" (I've debugged that). I think it's because the entity object is changed during the execution of Execute method (at the end the Status property is changed to "Processed") and Moq verifies the call against the changed object. In fact, this other unit test works well.

[TestMethod]
public void ShouldUpdateEntityWithStatusProcessedAtTheEnd()
{
    var mock = new Mock<IRepository>();
    var processor = new Processor(mock.Object);
    processor.Execute("test");
    mock.Verify(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Processed")), Times.Once());
}

我发现进行第一次单元测试的唯一方法是使用以下变通办法.我使用Moq的回调功能保存Status属性的值,并在以后进行断言.

The only way I found to make my first unit test works is using the following workaround. I save the value of Status property using a callback feature of Moq, and assert that later.

[TestMethod]
public void ShouldSaveEntityWithStatusInitialized_withWorkaround()
{
    var mock = new Mock<IRepository>();
    var processor = new Processor(mock.Object);
    string status = string.Empty;
    mock.Setup(m => m.InsertEntity(It.IsAny<Entity>())).Callback((Entity e) => status = e.Status);
    processor.Execute("test");
    Assert.AreEqual("Initialized", status);
}

但是我不喜欢那样.我想知道是否有一种方法可以让Moq在执行STU(被测系统)的过程中而不是在所有执行完成后验证在 期间对模拟对象的调用.

But I didn't like that. I would like to know if there's a way to make Moq verify the calls made to the mock object during the execution of the STU (system under test), not after all execution is done.

谢谢

推荐答案

恕我直言,最后一种方法(您称其为"hack")是测试状态值的正确方法.对我来说,更清楚的是,您要在此测试中验证的是在调用InsertEntity方法时将状态设置为已初始化".

IMHO, The last approach (in which you call the "hack") is the correct approach to test the value of the status. It's more clear to me that what you want to verify in this test is that the status is set to "Initialized" at the time the InsertEntity method is called.

要与验证"一起使用的方法相对于要测试的内容更加晦涩难懂.您是否要确认调用了InsertEntity还是要测试该参数是否已初始化",或者甚至不知道这两个参数?而且,如果您想同时测试两者,那么实际上这将是两个不同的单元测试.

The approach you want to use with the Verify is more obscure as to exactly what you are testing. Are you wanting to confirm the InsertEntity is called or are you wanting to test that the parameter is "Initialized", or maybe even both, I don't know. And if you are wanting to test both then this would actually be two different unit tests.

您还可以执行以下操作...

You could also do the following...

mock.Setup(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Initialized")));

但是我也不喜欢这种方法,因为这意味着当状态不是"Initialized"的值时,将运行生产InsertEntity代码而不是模拟程序.单元测试仍然可能失败的可能性更大,但是从单元测试返回的断言失败消息则更加晦涩(由于真正的InsertEntity方法内部发生了某些事情而导致失败).回调确切说明了您在单元测试中要测试的内容.

But I do not like this approach either because it means the production InsertEntity code will run instead of your mock when the status is not a value of "Initialized". More then likely the unit test will still fail but the assertion failure message returned from the unit test is more obscure (the failure occurs because of something happening inside the real InsertEntity method). The callback states exactly what you are testing in the unit test.

这篇关于Moq-用测试执行期间更改的参数验证呼叫的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆