0

I'm creating a web API using ASP.NET Core, and I'm using SimpleInjector as my DI framework. I understand the basics of how to use SI with ASP.NET Core; my problem is more an architectural one.

I have a project with integration tests for the API project, in order to test the raw API endpoints and responses. Naturally, the test server (set up using Microsoft.AspNetCore.TestHost) should use the API project's real Startup class.

The problem lies in where to register mocks for the controllers' dependencies, because I don't want to have all the production implementations being registered when testing: Firstly, most of them are, of course, dependencies used by the production implementations of the controller dependencies I'll be mocking in the first place; and secondly, in case I update my controllers and forget to register mocks of the new dependencies, I want my code to fail (container verification) instead of silently using production dependencies that are present in the container.

Thus, the dependencies can't be registered in the Startup class. That's fine by me – I think I'd rather keep the composition root in a separate assembly referencing all other assemblies, anyway. AFAICS the ASP.NET Core project would need to reference this project, which exposes a single method that returns a pre-registered container that can be used in the Startup class, where it's needed to register e.g. the controller activator (and will undergo final validation).

But this begs the question: How to get the container – being already registered with all my application components (whether production implementations from the composition root project, or mocks from the integration test project) – into my Startup class?

My initial solution is to simply have a static property on the Startup class called e.g. Container, and assign that before using WebHostBuilder. This seems "pragmatically robust": The application will fail fast (NullReferenceException) if it's not set before the Startup class is run, and the property is only used during setup, so I don't really need to guard against it being set multiple times or being set to null or any such thing – if it's assigned before startup, it works, if not, it won't start.

Does this seem like a solid solution, or am I oblivious to any obvious ways this will will come back to bite me later on? Are there any better solutions?

cmeeren
  • 3,890
  • 2
  • 20
  • 50
  • You know that you can have multiple start classes and/or `ConfigureXxx`/`ConfigureServicesXxx` methods right (i.e. `StartupTesting` or `ConfigureServicesTesting`) to perform different (but still per application - not per integration test) DI registrations? – Tseng Jun 08 '17 at 16:11
  • I'm not sure I really understand the problem here, and I think I agree with Tseng's suggestion (his comment, certainly not his answer) here. You can split registration in multiple parts and have all production registrations in its own method, which you can prevent from being called when running your integration test site. Perhaps through a configuration switch? – Steven Jun 08 '17 at 20:18
  • Slightly related to your question is the following: https://stackoverflow.com/questions/9501604/ioc-di-why-do-i-have-to-reference-all-layers-assemblies-in-entry-application – Steven Jun 08 '17 at 20:18
  • Thanks for the tip on multiple startup classes. I know of it, and this may be my inexperience speaking, but as far as reasonably possible I'd like to stick with just one. Having two startup classes means I have to keep them in sync (apart from the intended differences), which could introduce bugs. My application is simple enough that I really only need to change the registrations between testing/production. And anyway, since I'll be registering mocks that are set up in the integration test, I need external access to the container the `Startup` class uses. `ConfigureXxx` won't get me there. – cmeeren Jun 09 '17 at 06:14

0 Answers0