如何创建和使用部分存根(以最小起订量)而不依赖于具体实现?

发布于 2024-09-26 09:08:12 字数 999 浏览 1 评论 0原文

我有使用 MoQ 创建部分存根的代码。我更喜欢与接口而不是具体实现进行交互,这样如果我有不同的接口实现,我就不必修改单元测试。

例如,我有一个工厂方法,例如:

private Mock<ISomeInterface> ISomeInterfaceStubFactory()
{
    return new Mock<SomeConcreteImplementation>();
}

这是调用该方法的代码:

var partialStub = ISomeInterfaceStubFactory();            
partialStub.Setup(m => m.MethodToStubOutThatMethodToTestCalls(It.IsAny<string>())).Returns(new List<SomeOtherObject>());
partialStub.CallBase = true;

var actualResult= partialStub.Object.MethodToTest();

Assert.That(actualResult, Is.EqualTo(expectedResult));

问题是,执行此操作时 ISomeInterfaceStubFactory 将无法编译。所以我将其更改为如下所示,但这样做似乎破坏了部分存根。实际实现的 MethodToStubOutThatMethodToTestCalls 操作被调用,而不是存根版本。基本上我试图将多态性与存根对象一起使用。有办法做到这一点吗?我希望我的单元测试不要与具体实现高度耦合。

private Mock<ISomeInterface> ISomeInterfaceStubFactory()
{
    return new Mock<SomeConcreteImplementation>.As<ISomeInterface>();
}

I have code that uses MoQ to create a partial stub. I'd prefer to interact with the interface instead of the concrete implementation so that I won't have to modify the unit test if I have a different implementation of the interface.

So for example, I have a factory method such as:

private Mock<ISomeInterface> ISomeInterfaceStubFactory()
{
    return new Mock<SomeConcreteImplementation>();
}

Here is the code that calls the method:

var partialStub = ISomeInterfaceStubFactory();            
partialStub.Setup(m => m.MethodToStubOutThatMethodToTestCalls(It.IsAny<string>())).Returns(new List<SomeOtherObject>());
partialStub.CallBase = true;

var actualResult= partialStub.Object.MethodToTest();

Assert.That(actualResult, Is.EqualTo(expectedResult));

The problem is that when doing this is that ISomeInterfaceStubFactory won't compile. So I changed it to be like below, but doing this seems to break the partial stub. The actual implemented MethodToStubOutThatMethodToTestCalls operation gets called, not the stubbed version. Basically I'm trying to use polymorphism with the stub object. Is there anyway to do this? I'd like my unit test to not be highly coupled to the concrete implementation.

private Mock<ISomeInterface> ISomeInterfaceStubFactory()
{
    return new Mock<SomeConcreteImplementation>.As<ISomeInterface>();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

缪败 2024-10-03 09:08:12

我认为您错过了模拟对象的要点。从具体实现返回模拟是没有意义的。这个想法是让被测试的类依赖于一些你可以模拟的接口或抽象。

I think you are missing the point of mock objects. Returning a mock from a concrete implementation makes no sense. The idea is to have the class under test depend on some interface or abstract which you could mock.

笑梦风尘 2024-10-03 09:08:12

根据您的澄清修改我的答案。我并不反对arootbeer,但我确实想了解你在做什么以及为什么它不起作用。

这是我认为您正在尝试做的一个简单示例。对于这两种具体实现,我的测试都通过了。这就是您想要做的事情吗?这个例子对您有用吗?

接口和类:

using System;

namespace ClassLibrary1
{
    public interface IFoo
    {
        string GetBaseString();
        string GetExtendedString();
    }
    public class Foo_A : IFoo
    {
        public virtual string GetBaseString()
        {
            return "Foo_A";
        }
        public virtual string GetExtendedString()
        {
            return GetBaseString() + "_Bar";
        }
    }
    public class Foo_B : IFoo
    {
        public virtual string GetBaseString()
        {
            return "Foo_B";
        }
        public virtual string GetExtendedString()
        {
            return GetBaseString() + "_Bar";
        }
    }
}

单元测试:

using System;
using Xunit;
using Moq;

namespace ClassLibrary1.UnitTests
{
    public class Class1
    {
        [Fact]
        public void GetExtendedString_ReturnsExtendedString()
        {
            var partialFoo = IFooFactory();

            partialFoo.Setup(x => x.GetBaseString()).Returns("Foo");
            partialFoo.CallBase = true;

            string result = partialFoo.Object.GetExtendedString();

            Assert.Equal("Foo_Bar", result);
        }

        private Mock<IFoo> IFooFactory()
        {
            return new Mock<Foo_A>().As<IFoo>();
            //return new Mock<Foo_B>().As<IFoo>();
        }
    }
}

Revising my answer per your clarification. I don't disagree with arootbeer, but I do want to understand what you are doing and why it doesn't work.

Here's a simple example of what I think you are trying to do. The test passes for me for both concrete implementations. Is this what you are trying to do, and does this example work for you?

Interface and classes:

using System;

namespace ClassLibrary1
{
    public interface IFoo
    {
        string GetBaseString();
        string GetExtendedString();
    }
    public class Foo_A : IFoo
    {
        public virtual string GetBaseString()
        {
            return "Foo_A";
        }
        public virtual string GetExtendedString()
        {
            return GetBaseString() + "_Bar";
        }
    }
    public class Foo_B : IFoo
    {
        public virtual string GetBaseString()
        {
            return "Foo_B";
        }
        public virtual string GetExtendedString()
        {
            return GetBaseString() + "_Bar";
        }
    }
}

Unit test:

using System;
using Xunit;
using Moq;

namespace ClassLibrary1.UnitTests
{
    public class Class1
    {
        [Fact]
        public void GetExtendedString_ReturnsExtendedString()
        {
            var partialFoo = IFooFactory();

            partialFoo.Setup(x => x.GetBaseString()).Returns("Foo");
            partialFoo.CallBase = true;

            string result = partialFoo.Object.GetExtendedString();

            Assert.Equal("Foo_Bar", result);
        }

        private Mock<IFoo> IFooFactory()
        {
            return new Mock<Foo_A>().As<IFoo>();
            //return new Mock<Foo_B>().As<IFoo>();
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文