nsubstitute / NSubstitute

A friendly substitute for .NET mocking libraries.
https://nsubstitute.github.io
Other
2.67k stars 263 forks source link

ForPartsOf<T> doesn't run real code in constructor #188

Closed ahausmann closed 7 years ago

ahausmann commented 9 years ago

Hi, I have a third-party class that calls a virtual method in its constructor. When I tried to create a substitute for the class the virtual method returned the default value (or in the case below string.Empty).

Example Code:

public class TestClass
{
    public TestClass()
    {
        Debug.WriteLine(GetFoo());
    }

    public virtual string GetFoo()
    {
        return "foo";
    }

    public static void Main(string[] args)
    {
        var a = Substitute.ForPartsOf<TestClass>();
        Debug.WriteLine(a.GetFoo());
    }
}

The code above prints the following to the output window


foo

but I expected to get

foo
foo

Edit: Grammar fixes

dtchepak commented 9 years ago

Hi @ahausmann, Thanks for the report and the great sample case. I think the problem is the proxy class is not fully constructed when the virtual method is called, so the GetFoo() call in the TestClass constructor returns null instead of the proxy being able to delegate it back to the base class. The guidelines from MSDN suggest not calling virtual members in constructors for this reason. I'm not sure the NSubstitute code can do much about this? (happy to be proven wrong :) )

ahausmann commented 9 years ago

Well, when I tested it using Moq it worked. And afaik both NSubstitute and Moq use the Castle Proxy library, but correct me if I'm wrong.

Testcode for Moq:

public class TestClass
{
    public TestClass() 
    {
        Debug.WriteLine (GetFoo ());
    }

    public virtual string GetFoo() {
        return "foo";
    }

    public static void Main (string[] args)
    {
        Mock<TestClass> mock = new Mock<TestClass> ();
        mock.CallBase = true;

        TestClass a = mock.Object ;
        Debug.WriteLine (a.GetFoo ());
    }
}

And the output:

foo
foo

So it doesn't seem to be a problem with having the virtual method call in the constructor.

P.S. Yes I'm aware that it's bad to call virtual methods, but as I said it's a third-party class that I can't change.

dtchepak commented 9 years ago

Yes both use Castle Dynamic Proxy.

Does new Mock<TestClass> call the TestClass constructor? Or does that happen when mock.Object gets called? On 27 Jun 2015 1:46 am, "Alexander Hausmann" notifications@github.com wrote:

Well, when I tested it using Moq https://github.com/Moq/moq4 it worked. And afaik both NSubstitute and Moq use the Castle Proxy library, but correct me if I'm wrong.

Testcode for Moq:

public class TestClass { public TestClass() { Debug.WriteLine (GetFoo ()); }

public virtual string GetFoo() {
    return "foo";
}

public static void Main (string[] args)
{
    Mock<TestClass> mock = new Mock<TestClass> ();
    mock.CallBase = true;

    TestClass a = mock.Object ;
    Debug.WriteLine (a.GetFoo ());
}

}

And the output:

foo foo

So it doesn't seem to be a problem with having the virtual method call in the constructor.

P.S. Yes I'm aware that it's bad to call virtual methods, but as I said it's a third-party class that I can't change.

— Reply to this email directly or view it on GitHub https://github.com/nsubstitute/NSubstitute/issues/188#issuecomment-115736996 .

ahausmann commented 9 years ago

Sorry for the late response.

The constructor is called when the mock.Object property is called the first time.

dtchepak commented 9 years ago

I had a more detailed look at this. We deliberately disabled interception of calls during construction to fix issue #57.

It may be possible to pass through whether we are creating a partial sub proxy, and call base by default during constructor of the proxy for those cases, then swap back to the standard interceptor behaviour. (The proxy is created in CastleDynamicProxyFactory.cs.)

dtchepak commented 7 years ago

Closing as part of a general cleanup of issues. Please re-open if any action is required.