Bo Huang
Bo Huang

Reputation: 375

How to use Moq to Mock an internal class protected method?

I have classes below:

public class TestService
{
     public void Upload()
     {
        var manager = new Manager();
        var worker = manager.CreateWorker();
        worker.DoWork();
     }
}

public class Manager
{
    public Worker CreateWork()
    {
        return new Worker();
    }
}

public class Worker()
{
     public void DoWork()
     {
        SetupEnvironment();
        //some codes here...
     }

     protected virtual void SetupEnvironment()
     {
       //some codes here ...
     }
}

I want to use Moq to create an unit test for Upload() method in TestService class, and SetupEnvironment() method of Worker should be mock up. I tried to the code below, but It doesnt' work:

[TestMethod]
public void Upload_Test()
{
        var mockWorker = new Mock<Worker>();
        mockWorker .Protected().Setup<string>("SetupEnvironment").Returns(() =>
        {
            //some dummy code here ...
        });

        var service = new TestService();
        service.Upload();
        Assert.IsTrue(true);
  } 

Anybody knows how to make the above unit test code work?

Upvotes: 1

Views: 679

Answers (1)

gembird
gembird

Reputation: 14053

some things need to be changed. See the code of the class TestService and Worker below.

public class TestService
{
    private readonly Manager _manager;

    public TestService(Manager manager)
    {
        _manager = manager;
    }

    public void Upload()
    {
        var worker = _manager.CreateWorker();
        worker.DoWork();
    }
}

public class Manager
{
    public virtual Worker CreateWorker()
    {
        return new Worker();
    }
}

public class Worker
{
    public void DoWork()
    {
        SetupEnvironment();
        //some codes here...
    }

    protected virtual void SetupEnvironment()
    {
        //some codes here ...
    }
}

Then the test could look like this. The method SetupEnvironment is void so the setup can't return any value and you need to mock the Manager as well and pass it to the TestService e.g. via constructor injection so you can inject your mock-object. HTH

[TestMethod]
public void Upload_Test()
{
    bool setupEnvironmentWasCalled = false;

    Mock<Worker> mockWorker = new Mock<Worker>();
    mockWorker.Protected().Setup("SetupEnvironment")
        .Callback(() =>
        {
            //some dummy code here ...
            setupEnvironmentWasCalled = true;
        });

    Mock<Manager> mockManager = new Mock<Manager>();
    mockManager.Setup(m => m.CreateWorker())
        .Returns(mockWorker.Object);

    var service = new TestService(mockManager.Object);
    service.Upload();
    Assert.IsTrue(setupEnvironmentWasCalled);
} 

Upvotes: 1

Related Questions