【问题标题】:How can I Setup overloaded method invocations in Moq?如何在 Moq 中设置重载方法调用?
【发布时间】:2012-06-25 20:06:57
【问题描述】:

我正在尝试模拟映射接口IMapper

public interface IMapper<TFoo, TBar> {
    TBar Map(TFoo foo);
    TFoo Map(TBar bar);
}

在我的测试中,我将模拟映射器设置为期望调用每个映射器(围绕 NHibernate update 操作):

//...
_mapperMock.Setup(m => m.Map(fooMock.Object)).Returns(barMock.Object);
_mapperMock.Setup(m => m.Map(barMock.Object)).Returns(fooMock.Object);
//...

但是,当进行第二次 Map 调用时,映射器模拟会抛出,因为它只需要一次调用。

在运行时设置期间观看映射器模拟,我可以看到Map(TFoo foo) 重载已注册,然后在设置Map(TBar bar) 重载时看到它被替换。

这是 Moq 处理设置方式的问题,还是在这种情况下我需要使用不同的语法?

编辑 下面是来自测试构造函数的实际实例化代码:

public class TestClass {
    private readonly MockRepository _repository = new MockRepository(MockBehavior.Strict);

    public TestClass() {
        //...
        _mapperMock = _repository.Create
            <IMapper<RequestData.Foo, ResponseData.Bar>>();
        //...
     }
}

编辑 2

这是一个完整的失败测试用例:

public interface IMapper<TFoo, TBar> {
    TFoo Map(TBar bar);
    TBar Map(TFoo foo);
}

public class Foo {
    public override int GetHashCode() {
        // return base.GetHashCode();
        return 1;
    }
}

public class Bar {
    public override int GetHashCode() {
        // return base.GetHashCode();
        return 2;
    }
}

[Test]
public void TestIt()
{
    // Arrange
    var _mapperMock = new Mock<IMapper<Foo, Bar>>(MockBehavior.Strict);
    var fooMock = new Mock<Foo>();
    var barMock = new Mock<Bar>();

    _mapperMock.Setup(m => m.Map(fooMock.Object)).Returns(barMock.Object);
    _mapperMock.Setup(m => m.Map(barMock.Object)).Returns(fooMock.Object);

    // Act - breaks on first line below this comment
    var bar = _mapperMock.Object.Map(fooMock.Object);
    var foo = _mapperMock.Object.Map(barMock.Object);

    // Assert
    _mapperMock.Verify(x => x.Map(fooMock.Object), Times.Once());
    _mapperMock.Verify(x => x.Map(barMock.Object), Times.Once());
}

如果我在FooBar 或两者上注释掉GetHashCode() 覆盖,则测试用例通过。或者,如果我不使用FooBarMocks,则测试用例通过。

编辑 3 我针对这个问题打开了Moq Issue 347,有更详细的测试用例。

【问题讨论】:

  • 能否请您在实例化_mapperMock 的位置包含您的代码?谢谢。
  • 我唯一能看到您上面的代码失败的情况是通用 TFoo 和 TBar 是同一类型。例如。 var _mapperMock = new Mock>();如果它们不同(例如 var _mapperMock = new Mock>();),它应该可以工作
  • 它们是不同的类型,继承自不同的子类型。当我在调试器中单步执行测试时,我可以看到方法分辨率正确地变化,但是当第二次调用 Setup 时,第一次调用被第二次调用简单地替换了。我也会添加实际的实例化代码。

标签: c# moq overloading


【解决方案1】:

不确定您的 FooBar 类是什么样的,但我通过了这个测试(Moq 4.0.10827.0,这是 NuGet 中的最新版本)

using Moq;
using NUnit.Framework;

namespace ConsoleApplication1
{
    [TestFixture]
    public class Tests
    {
        [Test]
        public void TestIt()
        {
            // Arrange
            var _mapperMock = new Mock<IMapper<Foo, Bar>>();
            var fooMock = new Mock<Foo>();
            var barMock = new Mock<Bar>();

            _mapperMock.Setup(m => m.Map(fooMock.Object)).Returns(barMock.Object);
            _mapperMock.Setup(m => m.Map(barMock.Object)).Returns(fooMock.Object);

            // Act
            var bar = _mapperMock.Object.Map(fooMock.Object);
            var foo = _mapperMock.Object.Map(barMock.Object);

            // Assert
            Assert.AreSame(bar, barMock.Object);
            Assert.AreSame(foo, fooMock.Object);

            _mapperMock.Verify(x => x.Map(fooMock.Object), Times.Once());
            _mapperMock.Verify(x => x.Map(barMock.Object), Times.Once());
        }
    }

    public class Bar
    {
    }

    public class Foo
    {
    }
}

【讨论】:

  • 模拟的默认行为是Loose。如果_mapperMock 使用Strict 行为,上面的测试是否仍然通过?
  • 我用var _mapperMock = new Mock&lt;IMapper&lt;Foo, Bar&gt;&gt;(MockBehavior.Strict);测试了上面的内容,它仍然通过了。
  • 有趣...我想知道它是否与使用MockRepository 定义映射器有关。我会尝试直接实例化_mapperMock 看看是否有什么不同。
  • 在我的情况下,我仍然使用来自网站的 DLL(否则我们使用从源 AFAICT 构建的副本),并使用映射器的本地实例化模拟。但是,我发现它与FooBar 的基类有某些东西 - 如果我在没有指定基类的情况下运行测试,它将通过,但如果我指定FooBar 扩展各自的基类,测试失败。
  • 您发布了FooBar 的代码及其导致其失败的基本类型?
猜你喜欢
  • 2012-07-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 2015-12-14
  • 2011-10-13
  • 1970-01-01
  • 1970-01-01
  • 2011-01-30
相关资源
最近更新 更多