如何使用NSubstitute替换Object.ToString?

时间:2014-02-14 14:57:41

标签: c# nsubstitute

当我尝试使用NSubstitute 1.7.1.0来定义Object.ToString的行为(这是一个虚方法)时,NSubstitute会抛出异常。

重现:

[Test]
public static void ToString_CanBeSubstituted()
{
    var o = Substitute.For<object>();
    o.ToString().Returns("Hello world");

    Assert.AreEqual("Hello world", o.ToString());
}

失败:

NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException : Could not find a call to return from.

Make sure you called Returns() after calling your substitute (for example: mySub.SomeMethod().Returns(value)),
and that you are not configuring other substitutes within Returns() (for example, avoid this: mySub.SomeMethod().Returns(ConfigOtherSub())).

If you substituted for a class rather than an interface, check that the call to your substitute was on a virtual/abstract member.
Return values cannot be configured for non-virtual/non-abstract members.

Correct use:
    mySub.SomeMethod().Returns(returnValue);

Potentially problematic use:
    mySub.SomeMethod().Returns(ConfigOtherSub());
Instead try:
    var returnValue = ConfigOtherSub();
    mySub.SomeMethod().Returns(returnValue);

有没有办法让上面的测试通过?

从我的天真测试中抛出异常是一个错误还是“按设计”?

2 个答案:

答案 0 :(得分:7)

NSubstitute基于Castle.Core库,使用动态代理拦截和管理呼叫。在两个框架中都禁止拦截Object类的方法。

  1. 它在Castle的IProxyGenerationHook默认实现中被抑制。您可以找到代码here。我认为有理由这样做。当然,可以实现自己的IProxyGenerationHook,允许Object类的方法拦截,但是......

  2. NSubstitute也抑制了对象方法的拦截,而NSubstitute的语法就是原因。 “NSubstitute记录对替换的调用,当我们调用返回时,它会抓取最后一次调用并尝试配置它以返回特定值。”假设我们有以下代码:

    var service = Substitute.For<IService>();
    var substitute = Substitute.For<object>();
    service.AMethod(substitute).Returns(1);
    

    我们在这里调用“AMethod()”,NSub拦截执行并生成其内部事物,假设将“替换”值添加到调用substitute.GetHashCode()的字典中。如果我们拦截“GetHashCode()”方法,那么它将是最后记录的调用。 NSub会将指定的返回值绑定到错误的位置。几乎不可能避免这样的事情。

答案 1 :(得分:5)

您将无法替换System.Object中的任何方法,因为.Net特别对待它。

例如,出于同样的原因,这也失败了:

[Test]
public void x()
{
    var o = Substitute.For<anything>();
    o.GetHashCode().Returns(3);
    Assert.AreEqual(4, o.GetHashCode());
}

但这很好用:

public class Anything {
    public virtual string Else() {
        return "wrong";
    }
}

[Test]
public void x() {
    var o = Substitute.For<Anything>();
    o.Else().Returns("right");
    Assert.AreEqual("right", o.Else());
}

抱歉,我无法提供更好的消息,但是在.Net中嘲弄低级别对象效果不佳。