在不指定类型的情况下使用FakeItEasy创建假的通用方法

15

我想知道是否有任何方法可以伪造所有可能类型(或指定子类型)的通用方法调用?

例如,假设我们有这个出色的IBar接口。

public interface IBar
{
    int Foo<T>();    
}

我可以伪造对此IBar的Foo调用的依赖关系,而不必指定T为任何特定类型吗?
[TestFixture]
public class BarTests
{
    [Test]
    public void BarFooDoesStuff()
    {
        var expected = 9999999;
        var fakeBar = A.Fake<IBar>();

        A.CallTo(() => fakeBar.Foo<T>()).Returns(expected);

        var response = fakeBar.Foo<bool>();

        Assert.AreEqual(expected, response);
    }
}

谢谢!

1个回答

24

我不知道有直接实现这个的方式。我认为DynamicProxy(FakeItEasy使用的)不支持开放泛型类型。然而,如果你感兴趣,有一个变通方法。

有一种方法可以指定对伪造对象上任何方法或属性的调用。请查看此通过测试的WhereWithReturnType部分:

[TestFixture]
public class BarTests
{
    [Test]
    public void BarFooDoesStuff()
    {
        var expected = 9999999;
        var fakeBar = A.Fake<IBar>();

        A.CallTo(fakeBar)
            .Where(call => call.Method.Name == "Foo")
            .WithReturnType<int>()
            .Returns(expected);

        var response = fakeBar.Foo<bool>();

        Assert.AreEqual(expected, response);
    }
}

不过,我还是很好奇它的用途。你有没有一个使用伪造接口作为依赖项的实际测试示例?


1
+1 这是一个不错的做法,但我仍然不确定需要以这种方式设置一个虚拟对象。单元测试通常应该关闭泛型类型参数。 - Adam Ralph
非常好!我没有具体的示例展示……更多的是出于好奇是否可以(轻松地)完成此类事情。也许可以使用反射来限制虚假设置中的通用类型? - Rokey Ge
@AdamRalph 是的,但它不一定要以静态方式执行此操作。例如,我正在使用SpecFlow,并且我有像When <type> with Id <id> is retrieved from repository这样的步骤。通过上述方法,我不必为每个<type>准备一个步骤绑定方法。 - Tomasz Pluskiewicz
我刚刚使用这个方法使得所有我的验证辅助函数在单元测试中都默认通过。 A.CallTo(validationService).WithReturnType<bool>().Returns(true); 如果需要的话,我可以稍后覆盖其中一个具有特定行为的方法。 - Buh Buh
2
如果您正在使用C#6,那么您可以通过使用“nameof”来更轻松地进行重构: A.CallTo(fakeBar).Where(call => call.Method.Name == nameof(fakeBar.Foo)).WithReturnType<int>().Returns(expected); - Lech Osiński
这是一个相当老的答案,但我有一个使用示例:我有一个IExporter,使用ISerializer来序列化各种对象的方法。在我的“Exporter”测试中,我不关心序列化器返回什么,但我不希望它返回空字符串。因此,我使用了A.CallTo(serializer).WithReturnType<string>().Returns(fixture.Create<string>());但是,如果我稍后向ISerializer添加一个非序列化方法,我的测试可能会出现问题。 - thomasb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接