我有一个测试(代码如下)来测试Method1调用Method2。我得到的例外是
当前的代理生成器无法拦截指定的方法 原因如下: - 无法拦截密封方法。
测试方法本身并未密封。但是,它在密封类(第三方类,我无法创建包装器以便正确模拟它 - 另一个问题的另一个主题)上有依赖。无论哪种方式,此时我不是要求FakeItEasy嘲笑密封的班级。在调试我的测试时,当调用依赖项时,我可以清楚地看到正在生成一个真实的对象,而不是假的。
然而,鉴于错误信息,我觉得它可能会以某种方式相关。
此外,我通过random blog post发现,使方法虚拟修复问题,允许测试通过。我试了一下它就有效了。但是我没有为什么它修复了它,而且无论如何,将方法保持为虚拟对我来说没有意义。就我而言,被测试的班级没有自己的孩子,即;没有孩子覆盖它的方法,所以我看不出有任何理由让它成为虚拟的。
我认为我没有理由让这个方法变得虚拟吗? FakeItEasy试图嘲笑那个密封的班级吗?
我真的不确定如何进行这项测试。
我的测试
[SetUp]
public void SetUp()
{
// Arrange
_service2 = A.Fake<Service2>(x => x.WithArgumentsForConstructor(
() => new Service2()));
_service1 = A.Fake<Service1>(x => x.WithArgumentsForConstructor(
() => new Service1(_service2)));
}
[Test]
public void when_Method1_executes_it_calls_Method2()
{
// Act
result = _service1.Method1();
// Assert
A.CallTo(() => _service2.Method2())
.WithAnyArguments()
.MustHaveHappened();
}
相关方法
public class Service1 : IService1
{
private readonly IService2 _service2;
public Service1(IService2 service2)
{
_service2 = service2;
}
public bool Method1()
{
using (var dependency = new MyDependency()) // third party sealed class
{
}
var x = _service2.Method2();
}
}
public class Service2 : IService2
{
public bool Method2() // making this virtual fixes the FakeItEasy exception
{
}
}
答案 0 :(得分:21)
虽然通常应用于类范围,但在这种情况下sealed指的是无法覆盖相关方法。将sealed
与方法一起使用仅在方法为override
时才有效 - 但是,首先不是虚拟的方法不能被覆盖,因此它们本身是隐式密封的。
这个错误引用的是它不能接受非virtual方法,因为它正在创建一个从给定类继承的动态类来执行这些拦截。在这样的水平上,它既不能确定非关键方法和密封方法之间的差异,也不能确定 - 它不能覆盖,因此不能插入适当的拦截。
答案 1 :(得分:11)
阿拉沃尔的答案很棒。我敦促你接受和/或投票。
但是还有另一种方法。将_service2
假冒IService2
。
然后您不必更改任何方法的签名(接口方法总是可覆盖/可拦截)。通常,伪造接口接口比具体(甚至是抽象)类更容易,并且它具有实际测试的良好效果,即您的协作类可以使用接口,而不仅仅是接口的特定实现。
虽然我在这里,但这部分与您的错误并不相关,但可能有助于让您的代码更清晰,因为您正在测试Service1
,我会不是假的;使用实际的Service1
实例。这使读者清楚地了解了实际测试的内容。伪装被测系统被广泛认为是代码气味。
如果您碰巧同时使用这两点,您的SetUp看起来会更像这样:
[SetUp]
public void SetUp()
{
// Arrange
_service2 = A.Fake<IService2>();
_service1 = new Service1(_service2);
}
你的测试应该通过。
答案 2 :(得分:0)
我刚刚在两天内追逐几乎完全相同的问题。布莱尔康拉德在界面层面伪装的解决方案对我有用,而且实际上也很有意义:
如果被测试的类没有依赖于另一个类,那么测试也不应该。因此,您伪造了接口,而不是实现接口的类。