通过接口引入对象以模拟它

Bring object through interface in order to mock it

我正处于面临以下问题的十字路口。比方说,我有以下 class

public class Foo
{
   internal Bar bar { get; }

   internal Foo(Bar bar)
     {
         this.bar = bar;
     }
}

我正在尝试使用 XUnitMoq 对其进行单元测试。我创建的初始测试是

[Theory]
[AutoMockData]
internal void CtorShouldCreateInstance(Bar bar)
{
   var sut = new Foo(bar);

   Assert.NotNull(sut);
}

这是一个有效的单元测试,但是通过将 Bar 作为参数传递,它带来了所有依赖项,使其成为 concrete 类型.我同事的建议是通过 InterfaceMock 接口带来 Bar 对象,但我我不知道该怎么做。

我想过制作接口,CreateBar 的方法及其所需参数,继承 Foo 的接口 class,实现该方法,然后将其添加到单元测试中,但我想得到澄清或批准,因为我不确定这是正确的方法。

在此先感谢您的帮助!

你必须从 IBar 派生出 Bar,这样你就可以使用像 FakeItEasy 这样的 Mocking 框架轻松地 Mock 它,这里是一个例子:

public interface IBar
{
    string SayHello();
}

public class Bar : IBar
{
    public string SayHello()
    {
        // A complex work to retrieve data from external resource
        return "Hello";
    }
}

public class Foo
{
    private readonly IBar _bar;

    public Foo(IBar bar)
    {
        _bar = bar;
    }

    public string Greeting()
    {
        return _bar.SayHello();
    }
}

现在您可以模拟 IBar 以 return 您想要的结果:

[TestFixture]
public class FooTests
{
    [Test]
    public void Bar_ShouldReturn_HiInsteadOfHello()
    {
        IBar fakeBar = A.Fake<IBar>();
        A.CallTo(() => fakeBar.SayHello()).Returns("Hi");

        Foo sut = new Foo(fakeBar);
        Assert.AreEqual(sut.Greeting(), "Hi");
    }
}

* 旁注: 您也可以直接模拟您的方法,而无需通过将其设为虚拟来从接口派生该方法,但作为最佳实践,最好改用接口:

public class Bar
{
    public virtual string SayHello()
    {
        // A complex work to retrieve data from external resource
        return "Hello";
    }
}

public class Foo
{
    private readonly Bar _bar;

    public Foo(Bar bar)
    {
        _bar = bar;
    }

    public string Greeting()
    {
        return _bar.SayHello();
    }
}

[TestFixture]
public class FooTests
{
    [Test]
    public void Bar_ShouldReturn_HiInsteadOfHello()
    {
        Bar fakeBar = A.Fake<Bar>();
        A.CallTo(() => fakeBar.SayHello()).Returns("Hi");

        Foo sut = new Foo(fakeBar);
        Assert.AreEqual(sut.Greeting(), "Hi");
    }
}

显然我所要做的就是传递 IBar 接口作为参数而不是正常的 Bar class ,因为 AutoMockData 处理了一切。

所以是的,答案是使 Foo class

public class Foo
{
   internal IBar bar { get; }

   internal Foo(Bar bar)
     {
         this.bar = bar;
     }
}

测试变成

[Theory]
[AutoMockData]
internal void CtorShouldCreateInstance(IBar bar)
{
   var sut = new Foo(bar);

   Assert.NotNull(sut);
}

感谢大家的帮助!