【问题标题】:Bring object through interface in order to mock it通过接口引入对象以模拟它
【发布时间】:2019-02-06 12:43:06
【问题描述】:

我正处于以下问题的十字路口。比方说,我有以下 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类,实现方法然后添加到单元测试,但我想得到澄清或批准,因为我不确定这是正确的方法。

提前感谢所有帮助!

【问题讨论】:

  • Bar 应该从像 IBar 这样的抽象派生出来,这就是应该注入依赖项的内容

标签: unit-testing asp.net-core moq xunit


【解决方案1】:

你必须从 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 以返回您想要的结果:

[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");
    }
}

【讨论】:

  • 嘿,感谢您的详细回答,但我使用的是 XUnit,所以我没有您在回答中公开的方法。虽然我想我确实理解了你想说的概念。
  • @Vlădel 是的,我在这个例子中使用了 NUnit,但这些测试框架之间没有太大区别,它们只是使用不同的属性名称。我希望你明白了主要的想法。
【解决方案2】:

显然,我所要做的就是将 IBar 接口作为参数而不是普通的 Bar 类传递,因为 AutoMockData 会处理所有事情。

所以是的,答案是制作 Foo

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);
}

感谢大家的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-17
    • 2017-09-10
    • 2011-08-09
    • 1970-01-01
    • 2012-03-02
    • 2017-11-06
    相关资源
    最近更新 更多