Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autofixture mocking an interface to return random results for testing purposes

The problem

I have a pretty big application that makes use of a bunch of other services. For testing scenarios I don't want my unit tests to rely on third party systems, so I want to replace the services with fakes or mocks, or whatever.

I've already done most of the hard labor, and replaced the concrete services with a IService. The concrete Service is wired in with a DI framework

Now I want to replace those with random generated fakes.

The code

Interface of example service:

public interface ISimpleService
{
    int Fizz(int input);

    string Buzz(string input);
}

Interface of example service factory:

public interface ISimpleServiceFactory
{
    ISimpleService Create();
}

Implementation simple as possible

public static void Main(string[] args)
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var service = fixture.Create<ISimpleService>();

    var fizzResult = service.Fizz(42);
    var buzzResult = service.Buzz("I'd like something random...");
}

This shows what I basically want. I just want autofixture to create some dynamic proxy object for me, with methods that return something random of the type specified in the interface...

Note that I've used AutoMoq here, because by default Fixture doesnt want to create objects from interfaces, but I've tried other frameworks too: FakeItEasy, AutoRhinoMock

Workaround

Implementation that kind of works by manually setting everything

public static void Main(string[] args)
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var serviceMock = fixture.Create<Mock<ISimpleService>>();

    // These two lines below cause the magic now
   serviceMock.Setup(x => x.Fizz(It.IsAny<int>())).Returns(fixture.Create<int>());
   serviceMock.Setup(x => x.Buzz(It.IsAny<string>())).Returns(fixture.Create<string>());

    var service = serviceMock.Object;
    var fizzResult = service.Fizz(42);
    var buzzResult = service.Buzz("I'd like something random...");
}

This does give me the desired result: fizzResult with a random int, buzzResult with a random string (a guid by default) However, this is only a small example, my actual service references are a lot bigger, with up to 100s of methods... (they are external soap services etc, can't help it) I don't want to set up everything manually, if theres a generic solution, that would be great...

An implementation for factories that kind of works by manually setting everything

So, as you might have noticed, I've also posted a ISimpleServiceFactory interface. This resembles the actual situation, since the actual concrete ISimpleService requires a bunch of configuration. So, if we'd use the kind of working solution, we'd come to this:

public static void Main(string[] args)
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var serviceFactoryMock = fixture.Create<Mock<ISimpleServiceFactory>>();

    var serviceMockDelegate = new Func<ISimpleService>(() =>
        {
            var serviceMock = fixture.Create<Mock<ISimpleService>>();
            serviceMock.Setup(x => x.Fizz(It.IsAny<int>())).Returns(fixture.Create<int>());
            serviceMock.Setup(x => x.Buzz(It.IsAny<string>())).Returns(fixture.Create<string>());

            return serviceMock.Object;
        });

    serviceFactoryMock.Setup(x => x.Create()).Returns(serviceMockDelegate);
    var service = serviceFactoryMock.Object.Create();

    var fizzResult = service.Fizz(42);
    var buzzResult = service.Buzz("I'd like something random...");
}

This seems to be getting a bit of a mess, and this is a pretty small interface. The actual services are many levels deep, with 100s of methods.

For methods in my code that require specific conditions I'll obviously still manually set those conditions, but everything else should be random values by default. Generating large amounts of random objects allows for a bit of fuzzy testing too

Is there a way to automatically generate random objects without all this manual setting up?

like image 427
Ron Sijm Avatar asked Oct 02 '13 20:10

Ron Sijm


1 Answers

You don't need a factory nor do you need to setup every method within your interfaces, if I understand correctly you simply want to use Fixture to create a proxy which returns random values for every method you invoke on that proxy. Instead of using AutoMoqCustomization use AutoConfiguredMoqCustomization. It's all in the nuget package called Fixture.AutoMoq.

class Program
{
    static void Main(string[] args)
    {
    }
}

[TestFixture]
public class program
{
    [Test]
    public void some_test()
    {
        var fixture = new Fixture();
        fixture.Customize(new AutoConfiguredMoqCustomization());

        var simpleServices = fixture.CreateMany<ISimpleService>();

        foreach (var simpleService in simpleServices)
        {
            string randomString = simpleService.Buzz("hello");
            int randomInt = simpleService.Fizz(15);
        }
    }
}

public interface ISimpleService
{
    int Fizz(int input);

    string Buzz(string input);
}
like image 63
Ruskin Avatar answered Nov 12 '22 22:11

Ruskin