NSubstitute ForPartsOf...当调用真正的方法时

Kje*_*lbe 1 c# mocking nsubstitute

我正在尝试使用ForPartsOf<...>()and then subst.Configure().MyMethod(...).Returns(...)or替换一个方法subst.When(x => x.MyMethod(..)).Returns(...),但在这两种情况下,realMyMethod都会被调用。我的印象是,这两个下Configure()When()本来是要确保MyMethod()电话在“配置模式”时,使没有真正的通话将被制成。我错了吗?还是我做错了什么?

下面是我的(非常简化和名称更改的)代码。对于subst1and subst2,真正的NeedsMoreWork方法被调用 with item == null

public interface IMyClass
{
    bool NeedsMoreWork(Item item, out Part part);
    bool DoWork(Item item);
}

public class MyClass : IMyClass
{
    private ILogger log;

    public MyClass(ILogger log)
    {
        this.log = log;
    }

    public bool NeedsMoreWork(Item item, out Part part)
    {
        log.Debug($"Examining item {item.Id}");
        part = null;
        if (item.Completed())
        {
            log.Debug($"Item {item.Id} already completed.");
            return false;
        }
        part = item.GetNextPart();
        log.Debug($"Item {item.Id} needs work on part {part.Id}.");
        return true;            
    }

    public bool DoWork(Item item)
    {
        if (!item.NeedsMoreWork(item, out Part part))
            return false;
        log.Debug($"Starting work on part {part.Id}.");
        // Do work on part.
        log.Debug($"Work completed on part {part.Id}.");
        return true;
    }
}

[TestMethod]
public void TestDoWork()
{
    // Version with Configure():
    var subst1 = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
    subst1.Configure()
        .NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>())
        .Returns(false);

    // Version with WhenForAnyArgs():
    var subst2 = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
    subst2.WhenForAnyArgs(x => x.NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>())
        .Returns(false);
}
Run Code Online (Sandbox Code Playgroud)

Nko*_*osi 5

使用 NSubstitute 时,需要模拟/存根的成员virtual才能被覆盖。

参考部分潜艇和测试间谍

public class MyClass : IMyClass {
    private readonly ILogger log;

    public MyClass(ILogger log) {
        this.log = log;
    }

    public virtual bool NeedsMoreWork(Item item, out Part part) { //<-- THIS IS NOW VIRTUAL
        log.Debug($"Examining item {item.Id}");
        part = null;
        if (item.Completed()) {
            log.Debug($"Item {item.Id} already completed.");
            return false;
        }
        part = item.GetNextPart();
        log.Debug($"Item {item.Id} needs work on part {part.Id}.");
        return true;            
    }

    public bool DoWork(Item item) {
        if (!NeedsMoreWork(item, out Part part))
            return false;
        log.Debug($"Starting work on part {part.Id}.");
        // Do work on part.
        log.Debug($"Work completed on part {part.Id}.");
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

NeedsMoreWork 现在可以根据需要存根

[TestMethod]
public void TestDoWork_Should_Return_False() {
    //Arrange
    var subject = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
    bool expected = false;
    subject.Configure().NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>()).Returns(expected);

    //Act
    bool actual = subject.DoWork(new Item());

    //Assert - FluentAssertions
    actual.Should().Be(expected);
}
Run Code Online (Sandbox Code Playgroud)

请注意CAUTION注释。如果我们之前没有Configure()在这里使用过,.NeedsMoreWork(...)那么真正的方法就会在我们有机会覆盖行为之前执行。在某些情况下,这可能不是问题,但如果有疑问,请确保Configure()先调用,以便 NSubstitute 知道您正在配置调用并且不想运行任何实际代码。(这仍然不能保证真正的代码不会运行——记住,NSubstitute 不会阻止非虚拟调用的执行。)

Configure()方法仅在 NSubstitute 4.0 及更高版本中可用。对于 4.0 之前的版本,我们需要使用When .. DoNotCallBase

注意:强调我的

  • 默认情况下,接口是可重写的。你没有嘲笑界面。如果模拟一个类,则要模拟的成员必须是抽象的或虚拟的。 (2认同)