Thursday, April 19, 2018

Integration testing for controllers in .NET Core

First of all, in true fashion of posts on this blog, I haven't invented anything and if you're after that...sorry. What I've done is looking at a real challenge I had less that 1 year to the writing (Nov 2017) for which at the time we didn't have any solution (at least one that wouldn't cost an arm and a leg to implement), and finding a way to use new technology (.NET Core) to solve that issue. In short I've put 2 and 2 together and managed to do something that I believe is good practice.

I think I've mentioned in my last post how amazing .NET Core is, so I'm not going to repeat myself. .NET Core is amazing...just saying.

Consider this scenario.

Say you have an API that reads data from a database and this is developed in true TDD fashion. This implies the DB being abstracted behind a nice little interface and you have all the tests that make sense to ensure that your controllers behave correctly even if the database misbehaves.

That is all good and fun, but what happens for example when you want to introduce in your API some global filters, like authentication, exception handling and so on. Sure, you can unit test your filters all you want in isolation which is exactly what you should be doing.

But how do you know that when you put them all together within the MVC pipeline everything works as expected? Trust the platform some say, you don't need to test how MVC works, if it works once it will work for ever.

I love this approach ... not my problem, blame someone else. It makes life easy so you can focus on the "important" stuff.

However I'm sorry to say that that's not true. As long as a consumer uses your API, they don't care what technologies are used to build it, and if something goes wrong, they don't care that Microsoft screwed up. It's your API and if MVC all of the sudden misbehaves it's your fault that you haven't caught that issue.

So with this in mind, let's focus on that middle layer that so many forget about. The integration testing layer.

Integration testing a .NET MVC API like a black box (via tools like Postman) it's not easy to achieve for a number of reasons: a) for the MVC pipeline to kick in you have to host your API (most likely in IIS or IIS Express) and you can't do that within your C# code (not that I'm aware of, do let me know if I'm wrong) and b) once you start testing your hosted API usually your dependencies are not mocks anymore, but real systems that behave in an unpredictable manner.

Say we resolve the hosting bit, through a CD pipeline and you have an integration testing environment that it's sole purpose is to be spinned up, run some tests and then teared down. But what do we do about our dependencies? Do we start writing custom mocks ? Use service virtualization  (service virtualization in a nutshell is a proxy that captures and learns 3rd party APIs and you can start to program them, so that for specific requests you get a predicted response) ?

So let's focus a bit on how we can leverage .NET to allow us to write integration tests where we can control how our dependencies respond in order to have stable tests.

Recently I started building building a small personal project, and thinking API first I've decided to build and test the API and then think about the clients (web and mobile most probably). And because .NET Core is so cool (did I say that before?), I decided it might be a good chance to dig a bit deeper into what it can do.

First thing I did in my project, as a TDD advocate, was to set my unit test projects and then the actual API. A repository interface down the line it's time to start writing my first tests. All good and fun, nothing special up until now. However while doing my research I stumbled upon this official Microsoft documentation about integration testing in Core.

In a nutshell, the documentation says that every Core project is really a console app that does stuff, and in case of MVC the console app actually spins up a Kestrel server, and that Microsoft has built a NuGet package that can mock this server. This means you have control over the server configuration, as well as the MVC pipeline.

Consider this piece of code:

        
        public InitIntegrationTests()
        {
            _autoMocker = new AutoMocker();
            _server = new TestServer(new WebHostBuilder()
                .ConfigureServices(ConfigureServices)
                .UseSetting("Environment", "Production")
                .UseStartup<Startup>());
            _client = _server.CreateClient();
        }

        private void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IRepository>(_ => _autoMocker.GetMock<IRepository>().Object);
        }

        [Fact]
        public async void GetAll_ShouldSucceed()
        {
            var data = _modelMocker.CreateMany<MyDataObj>();
            _autoMocker.Setup<IRepository>(x => x.Get()).Returns(Task.FromResult(data));
            var response = await _client.GetAsync("/MyDataObj");
            response.EnsureSuccessStatusCode();

            var responseContent = await response.Content.ReadAsStringAsync();

            responseContent.ShouldNotBeNull();
            responseContent.ToLower().ShouldBe(JsonConvert.SerializeObject(data).ToLower());
        }

Do you see the magic? So let's break it down, but let's start the wrong way in, and look at the test first.

I mean, normal unit test stuff...you create a mocked list of objects and then you ensure that your mock repository returns them back when calling the Get method. But then, instead of me calling a controller function, what I actually do is a HTTP Get request to my test server. This ensures that the MVC pipeline kicks in and goes through all it's stages until it calls the controller to get the response. Then my test reads the content of the HTTP Get response and compares it to the mocked list and ensures it's correct.

So how does this work? Well, if you look in the InitIntegrationTests method you might spot a few different things from the blog I told you about. First of all, after I instantiate a new WebHostBuilder, I invoke it's ConfigureService function and point it to my very own. The reason I'm doing that is that I want my integration tests to override the normal DI registry with mocked implementations rather that actual ones. Furthermore I am overriding the Environment property so it uses production configuration and lastly I tell the builder to take anything else it can find in the Startup class.

I want to focus a bit more on the ConfigureServices part, because that's the tricky bit. You see by default the built-in .NET DI engine will always consider the last registry entry it finds for a particular type, so to help us the .NET Core guys have given us the TryAdd* functions. This means that my Startup ConfigureServices method looks like this:

        public void ConfigureServices(IServiceCollection services)
        {
            services.TryAddScoped<IRepository<League>>(_ => new TestDB());
            services.AddMvc(opts =>
            {
                opts.Filters.Add<GlobalExceptionFilter>();
            });
        }

This approach helps in the way that when you run your application the dependency registered for my interface comes from the Startup class, and while running integration tests I get full control to inject my dependencies.

So with that, I was able to write my whole API without having any DB repository working and linked to a DB, and my confidence is very high that once I add that component the whole system.

Friday, August 25, 2017

Mocking extension methods

I know it's been a while since I've blogged about anything, reason for this is that I've taken on the challenge of adjusting to the .NET Core world. And I must say it's a nice world to be in, and sometimes I find it quite hard to go back to the ol' MVC.

Getting back to the reason for this blogpost, which is around how to mock extension methods in your unit tests, let's consider the following example (please don't judge on the "code quality", this is for test purpose only).

Controller:

    public class HomeController : Controller
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private ISession _session => _httpContextAccessor.HttpContext.Session;
        public HomeController(IHttpContextAccessor httpContext)
        {
            _httpContextAccessor = httpContext;
            _session.SetString("test", "test");
        }
        public IActionResult Index()
        {
            ViewData["Session"] = _session.GetString("test");
            return View();
        }
    }
Unit test:

    public class UnitTest1
    {
        Mock<IHttpContextAccessor> _mockHttp = null;
        Mock<ISession> _mockSession = null;
        HomeController _sutController = null;
        public UnitTest1()
        {
            _mockHttp = new Mock<IHttpContextAccessor>();
            _mockSession = new Mock<ISession>();
            _mockHttp.Setup(x => x.HttpContext.Session).Returns(_mockSession.Object);
            _sutController = new HomeController(_mockHttp.Object);
        }
        [Fact]
        public void Test1()
        {
            _mockSession.Setup(x => x.GetString(It.IsAny<string>())).Returns(string.Empty).Verifiable();
            var result = _sutController.Index();
            Assert.IsType<ViewResult>(result);
            _mockSession.VerifyAll();
        }

Pretty standard and simple no? You have a controller that receives the IHttpContextAccessor instance (thank you .NET Core), which is a wrapper on top of ... ahem ... HttpContext. The Index action will just put in the ViewData whatever we have in the Session under the "test" key (which we add in the constructor but nevermind that ... i'm trying to make a point).

In the unit tests we mock the IHttpContextAccessor type and when the Session property is called we tell it to return another mocked ISession instance. And in our amazing test we just call the Index action and test that the GetString method from Session is called and that the controller generates a ViewResult.

Again, simple, standard and the outcome quite obvious:


Not really what you'd expect, and this brings us to the topic from the title of the blogpost. 

I've done a bit of digging around and realised that even if at compile time the method GetString is there, since it's an extension method, at runtime the mocking framework will have issues trying to mock it since it doesn't belong to the ISession type.

So what would the solution be?

Well, after a few good hours spent on a search engine (Google and Stackoverflow) was still stuck. However thanks to a suggestion from a colleague of mine, I've started digging up in the .net core code base for the extension method, to see the implementation.

So in this case, GetString (extension method) calls Get (extension method) that calls the TryGetValue which belongs to the ISession interface. So why not mock that and hope for the best?

Updated unit test:

        [Fact]
        public void Test1()
        {
            byte[] sessionValue = Encoding.UTF8.GetBytes("test");
            _mockSession.Setup(x => x.TryGetValue(It.IsAny<string>(), out sessionValue)).Returns(true).Verifiable();
            var result = _sutController.Index();
            Assert.IsType<ViewResult>(result);
            _mockSession.VerifyAll();
        }

And the outcome is the magic green tick.

So the takeaway from this is that if you want to mock extension methods, don't try it, it won't work anyway. Instead mock the method belonging to the type that the extension method calls.