moq/labs

Moq does not mock 'Explicit Interface Implementation' and 'protected virtual' correctly

Closed this issue · 1 comments

If we use Explicit Interface Implementation, with same name as a protected virtual method, Moq will mock the wrong method.
Protected methods should not be possible to mock, due to their protection level.

We found this issue in one of our Custom HttpHandlers that inherits from HttpClientHandler.
The method we implement is a overridden protected virtual method SendAsync. To be able to mock this, we used a Interface method as a layer in between for calling base (do an actual httprequest instead for just calling the mock).

Steps to Reproduce

Code example of code with this behaviour:

void Main()
{
    var mock = new Moq.Mock<Contract>() { CallBase = true };
    mock.As<IContractOveride>().Setup(m => m.DoWork()).Returns(3);
    Console.WriteLine(mock.Object.PublicWork()); //Writes out "3". Should be "1". Wrong method is mocked.
}

public class Contract : IContractOveride
{
    public int PublicWork()
    {
        return this.DoWork();
    }
    
    protected virtual int DoWork()
    {
        //Should not be able to mock this method: '...' is inaccessible due to its protection level
        ((IContractOveride)this).DoWork(); //Does not return the value of interface method.
        return 1;
    }

    int IContractOveride.DoWork()
    {
        Console.WriteLine("IFace");
        return 2;
    }
}

public interface IContractOveride
{
    int DoWork();
}

Working code:

void Main()
{
    var mock = new Moq.Mock<Contract>() { CallBase = true };
    mock.As<IContractOveride>().Setup(m => m.DoWorkOverride()).Returns(3);
    Console.WriteLine(mock.Object.PublicWork()); //Writes out "1" as expected
}

public class Contract : IContractOveride
{
    public int PublicWork()
    {
        return this.DoWork();
    }
    
    protected virtual int DoWork()
    {
        //Should not be able to mock this method: '...' is inaccessible due to its protection level
        ((IContractOveride)this).DoWorkOverride(); //Does not return the value of interface method.
        return 1;
    }

    int IContractOveride.DoWorkOverride()
    {
        Console.WriteLine("IFace");
        return 2;
    }
}

public interface IContractOveride
{
    int DoWorkOverride(); //This name is the only change.
}

Expected Behavior

It should write out "1" as i am overriding the interfaced method and not the virtual one.
The virtual method should not be mocked in any circumstances, as this one is protected.

Actual Behavior

It writes out "3" since it mocks the protected virtual method, and not the interfaced method.

Tested in Moq 4.9 and not 5+