Clever Human Clever Human - 3 months ago 21
C# Question

Moq an object created inside the method being tested

In the following example, I want to test the

TestMe.DoSomething()
function.

I want to mock the
ISomething
interface that is used within this method and make it return different values (depending on the specific unit test.)

In real life the
ISomething
interface winds up calling out to expensive 3rd party resources -- I definitely don't want to just call a real
ISomething
.

Here is the example structure:

class TestMe
{
public void DoSomething()
{
ISomething s = SomethingFactory();
int i = s.Run();

//do things with i that I want to test
}

private ISomething SomethingFactory()
{
return new Something();
}
}

interface ISomething
{
int Run();
}

class Something : ISomething
{
public int Run()
{
return 1;
}
}


Here is code that doesn't work:

var fakeSomething = new Mock<ISomething>();
var testMe = new TestMe();
Mock.Get(testMe).Setup(p => p.SomethingFactory()).Returns(fakeSomething.Object);
testMe.DoSomething();


Because
SomethingFactory()
is
private
, I cannot set the return value from that method to be what I want.

Any advice on how I can solve this?

Answer

Make the factory a full interface / class and remove the SomethingFactory method from TestMe.

public interface ISomethingFactory {
  ISomething MakeSomething();
}

public sealed class SomethingFactory {
  public ISomething MakeSomething() {
    return new Something();
  }
}

class TestMe
{
    private readonly ISomethingFactory _somethingFactory;

    public TestMe(ISomethingFactory somethingFactory) {
      _somethingFactory = somethingFactory;
    }

    public void DoSomething()
    {
        ISomething s = _somethingFactory.MakeSomething();
        int i = s.Run();

        //do things with i that I want to test
    }
}

This will allow you to mock ISomethingFactory to return a mock of ISomething.

While I think you may protest this solution as too drastic a change, I think its better than making a class that's not sealed with a members who's only reason for being virtual is for testing.

Comments