4

The problem I try to solve:

How to test client code that calls REST together with real REST server code (in an efficient way)?

Goals:

  1. It would be kind of integration test, but fast and easy

  2. It would allow to detect e. g. bug where client calls REST method with wrong verb (e. g. PUT, but API accepts POST)

  3. I find it overcomplicated to make integration tests with deployment of REST service and then using my API library to call, so I think of replacing it with in-memory unit/integration tests

  4. I find Flurl library useful, so I would like to use it in such test.

  5. Inspiration: https://stackoverflow.com/a/37510032/1453525

My idea: I would like to make my Flurl Api tests against real ApiController running in memory (using HttpServer), not using Flurl HttpTest. I found this technique here: https://stackoverflow.com/a/37510032/1453525 However it requires to build HttpRequest message each time and does not have nice Api for testing. Is it possible to run Flurl code against real in-memory ApiController tests?

Todd Menier
  • 37,557
  • 17
  • 150
  • 173
one_mile_run
  • 3,264
  • 4
  • 26
  • 29
  • Internally Flurl is using `HttpClient` so you can still configure it to use one that was generated by in-memory `HttpServer` – Nkosi Jul 17 '17 at 10:46

1 Answers1

3

I have not tested this, but I believe you should be able to use Flurl with in-memory hosting by creating a custom HttpClientFactory and overriding CreateMessageHandler:

public class TestingClientFactory : DefaultHttpClientFactory
{
    public overrride HttpMessageHandler CreateMessageHandler()
    {
        var config = new HttpConfiguration();
        //configure web api
        WebApiConfig.Register(config);

        return new HttpServer(config);
    }
}

In your test assembly, register this globally, ideally running just once on test fixture setup:

FlurlHttp.Configure(settings => settings.HttpClientFactory = new TestingClientFactory());

Now every time Flurl needs to create a new HttpClient in your tests, it will use the in-memory server.

Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • 1
    Based on my review of the source repo this should work. Depending on where the controllers are defined they may need to call the `WebApiConfig.Register` from the Presentation layer and apply it to the `config` – Nkosi Jul 18 '17 at 01:02
  • @Todd Menier: I setup this code, however it is no difference than without TestingClientFactory. CreateMessageHandler() is not executed (not hit in debugger). I have no idea why. I use HttpTest(). – one_mile_run Jul 31 '17 at 08:14
  • 1
    @one_mile_run As I've said before, do not use `HttpTest`. That is specifically for faking all HTTP calls made with Flurl, which isn't what you want to do. You want to make real calls to your in-memory HttpServer. `CreateMessageHandler` isn't getting hit because the existence of `HttpTest` [causes it to be bypassed](https://github.com/tmenier/Flurl/blob/master/src/Flurl.Http/FlurlClient.cs#L176-L178). – Todd Menier Jul 31 '17 at 14:03
  • @ToddMenier: Ok, I understand now why I should not use `HttpTest`. I assumed I would use its `Assert API`, but it makes no sense since calls are not faked. After couple tries I got final test working. I had to tweak `TestingClientFactory` to register my DI resolver and setup configuration values. Still, I am not sure if `FlurlHttp.Configure()` will be ok for parallel tests, because it seems to be a global configuration. Is this a design decision? – one_mile_run Aug 02 '17 at 15:32
  • @ToddMenier: Also I get warning when running such test using NUnit3: `System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain. This can happen if the test(s) started a thread but did not stop it. Make sure that all the threads started by the test(s) are stopped before completion.` I think this is another issue for separate investigation, as it might be related with NUnit itself or my own code. – one_mile_run Aug 02 '17 at 15:42
  • 1
    @one_mile_run `FlurlHttp.Configure` should be called once at app startup. For an NUnit test suite, do it [here](https://github.com/nunit/docs/wiki/SetUpFixture-Attribute). – Todd Menier Aug 02 '17 at 17:05