Why does FakeItEasy throw this exception, and why does making the method virtual fix it?

EF0 picture EF0 · Jul 11, 2014 · Viewed 9.4k times · Source

I have a test (code is below) to test that Method1 calls Method2. The exception I'm getting is

The current proxy generator can not intercept the specified method for the following reason: - Sealed methods can not be intercepted.

The method under test isn't sealed itself. However, it does have a dependency on a sealed class (a third-party class for which I am having trouble creating a wrapper in order to mock it properly - another topic for another question). Either way, at this point I'm not asking FakeItEasy to mock the sealed class. And while debugging my test, when the dependency is called, I can clearly see that a real object is being produced, not a fake.

And yet, given the error message, I feel like it might be related somehow.

Further, I discovered through a random blog post that making the method virtual fixes the problem, allowing the test to pass. I gave it a try and it worked. But I don't get why it fixed it, and regardless, it doesn't make sense for me to keep the method virtual. In my case, the class being tested doesn't have any children of its own, ie; no children to override its methods, so I can't see any reason to make it virtual.

Am I wrong in thinking I have no reason to make the method virtual?
Is FakeItEasy somehow trying to mock that sealed class?

I'm really not sure how to proceed with this test.

My Test

[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();
}


Related Methods

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
    {            
    }
}

Answer

David picture David · Jul 11, 2014

While normally applied to the class scope, sealed in this case refers to the inability to override the method in question. Using sealed with a method is only valid if the method is an override - however, methods which are not virtual in the first place cannot be overidden, and are thus themselves implicitly sealed.

What this error refers to is that it cannot accept non-virtual methods, due to the fact that it is creating a class on the fly inherited from your given class to perform these intercepts. At such a level, it can neither determine the difference between a non-vritual and sealed method, nor does it need to - it cannot override, and thus cannot insert the appropriate intercept.