haroon latif
haroon latif

Reputation: 53

Owin hosted webapi 2.2. testing controller with mocked service

I have webapi which for testing purposes I am hosting in owin. I have set it up using autofac. now when I am testing I want to inject moq dependencies. which I am not able to so far. I have read the documentation and did bit of research but I am missing something.

here is the testing code.

 [Test]
    public void Request_all_airports()
    {
        const int port = 8086;
        AirportCollection moqAirportCollection = new AirportCollection();
        moqAirportCollection.Airports = new List<Airport>{new Airport{IATA = "moq",Name = "moqName"}};

        using (WebApp.Start<Startup>("http://localhost:" + port))
        {
            using (var mock = AutoMock.GetLoose())
            {
                var moqObj =  mock.Mock<IAirportService>().Setup(x => x.GetAirports()).Returns(moqAirportCollection);

                var client = new HttpClient {BaseAddress = new Uri("http://localhost:" + port)};
                var response = client.GetAsync("/api/airport/get").Result;
                var body = response.Content.ReadAsStringAsync().Result;
                var airportCollection = JsonConvert.DeserializeObject<AirportCollection>(body);
            }
        }
    }

Please have a look. let me know what I am missing. if you want to look at controller code or any other piece do let me know .

here is code for startup

 public class Startup
{
    public static IContainer container { get; set; }

    public void Configuration(IAppBuilder appBuilder)
    {
        var httpConfig = new HttpConfiguration();
        container = AutofacSetup.Register(httpConfig);
        WebApiConfig.Register(httpConfig);


        appBuilder.UseAutofacMiddleware(container);
        appBuilder.UseAutofacWebApi(httpConfig);
        appBuilder.UseWebApi(httpConfig);
    }
}

Thanks

I think so I have solved it with help from people. here is my code.

var moq = new Mock<IAirportService>();
                moq.Setup(x => x.GetAirports()).Returns(moqAirportCollection);
                newBuilder.RegisterInstance(moq.Object).As<IAirportService>();
                newBuilder.Update(Startup.container);

I havnt rebuild the contrain i just updated it. autofac have behavior to use latest registration so it will use mocked on here.

Upvotes: 1

Views: 2154

Answers (2)

Anish Patel
Anish Patel

Reputation: 4392

You are almost there.

In your test you need to register your mock service with your autofac container so that dependencies on IAirportService are resolved with the mock in the application.

One way to achieve this is override the Startup class' Configuration method for each test and put your test DI in there. I've put some comments below to show changes that can be made:

public class Startup
{
    public static IContainer container { get; set; }

    // make this virtual
    public virtual void Configuration(IAppBuilder appBuilder)
    {
        var httpConfig = new HttpConfiguration();

        // have this return the ContainerBuilder instead of the container
        var builder = AutofacSetup.Register(httpConfig)
        container = builder.Build();

        WebApiConfig.Register(httpConfig);


        appBuilder.UseAutofacMiddleware(container);
        appBuilder.UseAutofacWebApi(httpConfig);
        appBuilder.UseWebApi(httpConfig);
    }
}

Then in your test class, derive from the Startup class and put your test logic in. Something like this:

public class MyTestCase {
        public static Mock<IAirportService> MockObj { get; set; }

        private class TestStartup : Startup {
            public override void Configuration(IAppBuilder app) {
                var httpConfig = new HttpConfiguration();

                // this now returns ContainerBuilder instead of the container
                var builder = AutofacSetup.Register(httpConfig)

                // register your mock, change this to whatever lifetime scope you need
                var moqAirportCollection = new AirportCollection();
                moqAirportCollection.Airports = new List<Airport>{new Airport{IATA = "moq",Name = "moqName"}};
                var mock = AutoMock.GetLoose()
                MockObj = mock.Mock<IAirportService>()
                                           .Setup(x => x.GetAirports())
                                           .Returns(moqAirportCollection);
                var moqObj = MockObj.Object;
                builder.RegisterInstance(moqObj).As<IAirportService>();



                container = builder.Build();

                WebApiConfig.Register(httpConfig);


                appBuilder.UseAutofacMiddleware(container);
                appBuilder.UseAutofacWebApi(httpConfig);
                appBuilder.UseWebApi(httpConfig);
            }
        }

        [Test]
        public void Request_all_airports()
        {    
           using (var server = WebApp.Start<Startup>())
           {
               var response = 
                   server.CreateRequest("/api/airport/get")
                         .GetAsync()
                         .Result;

                var body = response.Content.ReadAsStringAsync().Result;
                var result = JsonConvert.DeserializeObject<AirportCollection>(body);

                // assert something
            }
        }
    }

Upvotes: 4

Cyril Durand
Cyril Durand

Reputation: 16192

A unit test should test a single component. In your case, you are trying to test the AirportController through a HTTP query, not the AirportController as a standalone component.

The AirportController class depends on a IAirportService component. In order to test the component without any dependency you created a moq on IAirportService. Now you can instantiate a new AirportController with this moq and run your test using this instance.

If you have a AirportController like this

public class AirportController
{
    public AirportController(IAirportService airportService) { /* ... */}
}

The AirportController test should be like this :

[Test]
public void Request_all_airports()
{
    AirportCollection moqAirportCollection = new AirportCollection();
    var moqAirPort = new Airport{ IATA = "moq",Name = "moqName" };
    moqAirportCollection.Airports = new List<Airport>{ moqAirPort };

    using (var mock = AutoMock.GetLoose())
    {
        var moqAirportService = mock.Mock<IAirportService>()
                                    .Setup(x => x.GetAirports())
                                    .Returns(moqAirportCollection);
        var testedAirportController = new AirportController(moqAirportService); 

        AirportCollection airportCollection = testedAirportController.Get();

        Assert.AreEquals(1, airportCollection.Length, "Invalid number of airport"); 
        Assert.AreEquals(moqAirPort.Name, airportCollection[0].Name, "Invalid name");
    }
}

Upvotes: 0

Related Questions