如何使用NSubstitute替换Object.ToString?

Mil*_*ian 9 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());
}
Run Code Online (Sandbox Code Playgroud)

失败:

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);
Run Code Online (Sandbox Code Playgroud)

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

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

Ale*_*tin 7

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

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

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

    var service = Substitute.For<IService>();
    var substitute = Substitute.For<object>();
    service.AMethod(substitute).Returns(1);
    
    Run Code Online (Sandbox Code Playgroud)

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


Iai*_*ard 5

您将无法替换任何方法,System.Object因为.Net非常特别地对待它.

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

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

但这很好用:

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());
}
Run Code Online (Sandbox Code Playgroud)

对不起,我无法提供更好的消息,但嘲笑低级别对象在.Net中效果不佳.

  • 不幸的是,这不是一种疏忽.'System.Object'对象不是普通的.Net类.它是垃圾收集器的根,IL中的分配标识符,它是一个非托管的COM对象.它深埋在.Net运行时中.我是该项目的贡献者之一,我可以说,一旦进入IL的深处,事情并不总是它们在C#中的样子.祝你好运! (2认同)