Monday, 19 March 2018

Mocking SignOutAsync and SignInAsync

It seems that despite Microsoft going some great distance on Dependency Injection in DotNet Core, there is still a lot of smoke and mirrors with the horrible blob they call HttpContext.

Mocking this in unit tests is doable but a bit of a pain. The easiest way is to set the ControllerContext.HttpContext to DefaultHttpContext, which provides some magic services out of the box, allowing things like The UrlHelperFactory and things to work as expected (or at least not to crash).

However there is a problem! It only provides default services for things that work out of the box with safe defaults. If you try and call SignOutAsync and SignInAsync, your test will give you the infamous: Message: System.ArgumentNullException : Value cannot be null. Parameter name: provider and the call stack will show ServiceProviderServiceExtensions.GetRequiredService()

There is a stack overflow answer here but it uses the deprecated AuthenticationManager class which is not great.

I played around and created a mock for the auth manager:

Then I needed to add a new RequestServices object (of type IServiceProvider), which is null by default in DefaultHttpContext (but don't be fooled because it handles things automagically!).

Request Services Mock Gist

BUT then when I ran all my tests, about 50 of them now failed, including the one I was "fixing" because now I was being told that IUrlHelperFactory and ITempDataDictionaryFactory were not being resolved! What? I hadn't touched them and the RequestServices property was null by default - what was happening?

I remember reading that DefaultHttpContext provides you some basic services by default - although unfortunately, it doesn't do that very obviously! By setting RequestServices, this magic service is obviously removed and things you used to get by default, you don't any longer!

Fortunately, there are default implementations which are easy enough to use for the additional 2 services, and hopefully if you are using any others, they will also have defaults or will be easy to mock:

Additional services to mock Gist

Post a Comment