如何模拟一个也属于目标类本身的方法?

Nam*_* VU 7 unit-testing moq mocking

假设我们正在测试C类,它有两种方法M1和M2,其中M1在执行时调用M2.

测试M2没问题,但我们如何测试M1呢?困难在于,如果我不误解事物,我们需要模拟M2.

如果是这样,我们如何在测试同一个类中定义的方法时模拟另一个方法?

[编辑]
C类没有基类.

Jef*_*ata 7

您可以通过将mock上的CallBase属性设置为来执行此操作true.

例如,如果我有这个类:

public class Foo
{
    public virtual string MethodA()
    {
        return "A";
    }
    public virtual string MethodB()
    {
        return MethodA() + "B";
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以设置MethodA并调用MethodB:

[Fact]
public void RunTest()
{
    Mock<Foo> mockFoo = new Mock<Foo>();
    mockFoo.Setup(x => x.MethodA()).Returns("Mock");
    mockFoo.CallBase = true;

    string result = mockFoo.Object.MethodB();

    Assert.Equal("MockB", result);
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*ton 6

您应该让调用M1传递给方法的实际实例M2.

通常,您应该测试类的黑盒行为.你的测试不应该关心M1调用M2- 这是一个实现细节.

这与模拟外部依赖项(您应该这样做)不同......


例如,假设我有一个这样的类:

class AccountMerger
{
    public AccountMerger(IAccountDao dao) 
    {
        this.dao = dao;
    }     

    public void Merge(Account first, Account second, MergeStrategy strategy) 
    {
        // merge logic goes here...
        // [...]
        dao.Save(first);
        dao.Save(second);
    }

    public void Merge(Account first, Account second) 
    {
        Merge(first, second, MergeStrategy.Default);
    }

    private readonly IAccountDao dao;
}
Run Code Online (Sandbox Code Playgroud)

我希望我的测试表明:

  1. 调用Merge(first, second, strategy)两个已保存的帐户的结果已使用提供的规则合并.

  2. 调用Merge(first, second)两个已保存的帐户的结果已使用默认规则合并.

请注意,这些要求都是根据输入效果来表达的- 特别是,我并不关心课程如何实现这些结果,只要它确实如此.

第二种方法碰巧使用第一种方法的事实不是我关心的事情,甚至我想强制执行 - 如果我这样做,我会写非常脆弱的测试.(甚至有一种说法是,如果你使用模拟框架搞乱了测试中的对象,你甚至不再测试原始对象,那么你测试什么?)这是一个内部依赖,可以很愉快改变而不违反要求:

    // ...
    // refactored AccountMerger methods
    // these still work, and still fulfil the two requirements above

    public void Merge(Account first, Account second, MergeStrategy strategy) 
    {
        MergeAndSave(first, second, strategy ?? MergeStrategy.Default);
    }

    public void Merge(Account first, Account second) 
    {
        // note this no longer calls the other Merge() method
        MergeAndSave(first, second, MergeStrategy.Default);
    }

    private void MergeAndSave(Account first, Account second, MergeStrategy strategy) 
    {
        // merge logic goes here...
        // [...]
        dao.Save(first);
        dao.Save(second);
    }

    // ...
Run Code Online (Sandbox Code Playgroud)

只要我的测试只检查输入和效果,我就可以轻松地进行这种重构 - 我的测试甚至可以帮助我这样做,因为他们确保我在进行更改时没有打破课程.

另一方面,我做的是在合并后AccountMerger使用IAccountDao保存帐户(尽管不AccountMerger应该关心DAO的实现,只是它有一个Save()方法.)这个DAO是模拟的主要候选者 - 外部该依赖性AccountMerger类,感觉的效果我要检查一定的投入.