当我尝试使用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);
有没有办法让上面的测试通过?
从我的天真测试中抛出异常是一个错误还是“按设计”?
答案 0 :(得分:7)
NSubstitute基于Castle.Core库,使用动态代理拦截和管理呼叫。在两个框架中都禁止拦截Object类的方法。
它在Castle的IProxyGenerationHook默认实现中被抑制。您可以找到代码here。我认为有理由这样做。当然,可以实现自己的IProxyGenerationHook,允许Object类的方法拦截,但是......
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中嘲弄低级别对象效果不佳。