JensOlsen112
JensOlsen112

Reputation: 1299

How to force method to use a mock?

My application consists of various concrete Job-classes (all inheriting from the abstract Job class). In their Run() method they usually call external services. I'd like to test my job classes and mock the service results.

Here's how a typical concrete Run() method looks:

    InstallWpJobResult result = new InstallWpJobResult();
    WpManager wpManager = new WpManager();
    if (!WpManager.InstallWp(Domain, SiteTitle, WpUsername, WpPassword, WpEmail))
        result.Error = "Error installing WordPress";
    return result;

I'd like the WpManager.InstallWp to return true (in the mocked version). I know how to mock the object and method but how do I make my job use the mocked version? (Right now it creates an instance within the method itself).

Btw I'm using the Moq framework for mocking.

Upvotes: 4

Views: 756

Answers (2)

rae1
rae1

Reputation: 6134

Instantiating concrete classes inside consumer classes goes against one of the SOLID principles (the "D" or "Dependency Injection"). One of the reasons is the one you ran across: testability.

I suggest you make the WpManager take an instance of IInstallWpJobResult (note it is an interface) as part of its construction, rather than instantiating one. This way you can easily change the behavior of the class given different IInstallWpJobResult implementations, e.g. passing in a Mock<IInstallWpJobResult> with predefined behavior for easier testing.

This will also come in handy once you start using Dependency Injection frameworks to resolve all those dependencies at runtime, since they usually look at the arguments of a constructor to get an idea of dependencies to inject.

Upvotes: 0

Dan Bryant
Dan Bryant

Reputation: 27495

A few options:

  1. Define a factory service IWpManagerFactory that is injected into your job class, with a method to construct your mocked WpManager.
  2. Define a virtual method BuildWpManager in your job and override the method in your test to inject your mock.

Quick example of the factory service approach:

public interface IWpManagerFactory
{
    WpManager BuildWpManager();
}

public sealed class Tests
{
    public void Test()
    {
        var manager = new Mock<WpManager>();
        //Set up mock manager here...

        var factory = new Mock<IWpManagerFactory>();
        factory.Setup(f => f.BuildWpManager()).Returns(manager.Object);

        //Inject factory to class under test and execute the method under test...
    }
}

This assumes of course that your WpManager has virtual methods that can be mocked. If not, you'll need to extract an IWpManager interface as well, then mock that instead.

Upvotes: 3

Related Questions