ASP.NET Testing with a Service Layer

Unit testing is becoming a norm for developers and ASP.NET is no different. Although TDD is baked into Ruby on Rails, and some JavaScript frameworks for quite some time now, ASP.NET testing is a new thing for some people.

I’ve recently started a new web project that has a testing setup that has grown into something that’s worth sharing. I’ve expressed my struggles with TDD in the past, but I dedicated myself to testing in this project, no matter the hurdles.

I want to show people that testing a well architected .NET app isn’t complicated and shouldn’t be hard. If thought through smartly, great test coverage is can be a valuable asset in any app.

Project Architecture

A competent ASP.NET testing setup comes as a by-product of a well thought-out, loosely coupled system. I had these aspects in mind when starting this project. Here’s a diagram of how it’s laid out:

Project Structure

The Web project is straightforward. It’s a vanilla ASP.NET project containing controllers and Quartz jobs (something I’ll touch on in a different post). I made it point to keep all business logic out of this project. The Web project is purposely kept lean.

Going to other end of the stack is the Data project. This only contains the Entity Framework Code-First models, DbContext, and migrations. Other than seeding the database, there’s no logic in this project either.

NinjectThe meat and potatoes of the app resides in the Services project. Most of the time, each entity has its own service, but sometimes if two entities are related enough, they get consolidated into a single service. Additionally, there’s DTO’s, DTO mappings, and Ninject bindings that all happen as part of this project.

Just to wrap up the overview, each service gets an injected DbContext, each controller that needs it, gets an injected service. I’m trying to decouple as much as I can, which is important for quality testing.

Unit Testing Services

Since I’m injecting the DbContext into each service, each service has full access to whatever data it needs. This could be replaced with some sort of repository layer to make mocking easier, but I figured the DbContext was enough of a repository layer that adding that extra layer didn’t buy me much. One could argue this allows each service to reach outside its bounds, but with a small development team, this is easy to manage.

Here’s what a small service looks like:

ServiceBase is an abstract class to bring in some shared properties that all services have. Mainly being the  DbContext and some loggers for debug messages. The interface is just for dependency injection purposes.

With that in mind, testing is made extremely easy. Each service has its own test class, with the initialize and clean up looking something like this:

I’m using Moq to create a mocked version of the DbContext which allows me to control what the DbSets return for data in the service.

Now that everything is setup, I can now write tests whilst controlling the return values. If I wanted to write some tests for finding an entity by key, I could do:

All of my service tests look fairly similar to this. I try to follow the Arrange, Act, Assert model when constructing these tests. I have some helpers to generate fake data. There’s numerous ways to do that, but make sure it’s something that will generate valid enough data to make the tests worth it. The other piece is converting the fake data to a DbSet that can be returned by the DbContext . I have a custom implementation to build a set in memory. If you’re interested in that code, I put it up in a Gist.

Unit Testing Controllers

Testing controllers in something not a lot of people do. If you make a push to keep them thin, then testing them might be slight overkill, but sometimes it’s not a bad idea. I showed how to create a mock HttpContext, which is important for testing controllers.

The setup is pretty similar to how services are tested:

Any services are mocked, an  HttpContext is created, and the controller is ready for testing.

From there you can test whatever you want that comes out of the controller. Most of the time I’m making sure that 404’s are returned in proper spots, or a particular view is returned under specific conditions. These tests are kept quick and short, but that requires again, keeping the controller thin.

Test Away

I wanted to get this whole process down and share it because sometimes getting a good testing flow in .NET can sometimes be tough and confusing. I know this may not be the most efficient way, or it doesn’t adhere to TDD completely, but it works for me and allows me to construct an API that is solid.