与Moq模拟通用,动态接口

时间:2014-03-17 21:05:52

标签: .net dynamic moq

我有以下界面来模拟:

internal interface IRenderer
{
    void Render<T>(StringWriter stringWriter, T item) where T : struct;

    void Render(StringWriter stringWriter, decimal item);

    void Render(StringWriter stringWriter, string item);
}

测试如下:

[Test]
[TestCase("dummy-string", "dummy-string")]
[TestCase(123, "123")]
[TestCase(123.456, "123.456")]
[TestCase(true, "True")]
[TestCase('C', "C")]
public void RendersExpectedValues(dynamic item, string rendered)
{
    // Do some stuff
    using (var stringWriter = new StringWriter(renderedBuilder)
    {
        _mockRenderer.Setup(x => x.Render(textWriter, item));
        _renderer.Render(textWriter, item);
    }

   Assert.It.Worked();
}

正如您所看到的,itemdynamic,重载是在运行时确定的......我玩过

Type type = item.GetType();
_mockRenderer.Setup(x => x.Render<type>(textWriter, item));

Type type = item.GetType();
_mockRenderer.Setup(x => x.Render<typeof(type)>(textWriter, item));

两者都没有(当然)。

这可能吗?我可以将字符串测试用例删除到单独的测试中。

1 个答案:

答案 0 :(得分:1)

我认为这并不是直接的 - 我总是得到:

  

错误:表达式树可能不包含动态操作`

尝试在安装程序中使用dynamic时。 AFAIK您需要在每种支持的类型上做明确的Setup()

但是,你可以稍微干一点,但是使用通用的设置辅助方法,例如:

   static class Helpers
   {
      public static Mock<IRenderer> Setup<T>(this Mock<IRenderer> mockRenderer) where T : struct
      {
         mockRenderer.Setup(x => x.Render(It.IsAny<StringWriter>(), It.IsAny<T>()))
            .Callback<StringWriter, T>((sw, s) => sw.Write(s));
         return mockRenderer;
      }
   }

然后可以设置+调用:

  [Test]
  [TestCase("dummy-string",  "dummy-string")]
  [TestCase(123, "123")]
  [TestCase(123.456, "123.456")]
  [TestCase(true, "True")]
  public void RendersExpectedValuesString(dynamic item, string rendered)
  {
     var _mockRenderer = new Mock<IRenderer>()
        .Setup<double>()
        .Setup<bool>()
        .Setup<int>()
        .Setup<decimal>();
     _mockRenderer.Setup(x => x.Render(It.IsAny<StringWriter>(), It.IsAny<string>()))
        .Callback<StringWriter, string>((sw, s) => sw.Write(s));

     var renderedBuilder = new StringBuilder();
     var sut = new SomethingWhichUsesRenderer(_mockRenderer.Object);

      // Do some stuff
      using (var stringWriter = new StringWriter(renderedBuilder))
      {
        sut.DoSomething(stringWriter, item);

        // This obviously just tests the Mock ...
        Assert.AreEqual(renderedBuilder.ToString(), rendered);

        // Do actual unit tests + Mock.Verifies here
      }
  }