11

当我尝试使用 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);

有没有办法使上述测试通过?

我的天真测试引发的异常是错误还是“设计使然”?

4

2 回答 2

9

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

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

  2. NSubstitute 还禁止拦截 Object 的方法,原因是 NSubstitute 的语法。“NSubstitute 记录对替代品的调用,当我们调用 Returns 时,它会抓取最后一次调用并尝试将其配置为返回特定值。” 假设我们有以下代码:

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

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

于 2014-02-14T19:27:27.600 回答
6

您将很难替换任何方法,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 中效果不佳。

于 2014-02-14T15:36:23.193 回答