TinRoll #3: Building Out the API Layer

Morgan Kenyon
The Startup
Published in
7 min readJul 13, 2019
Photo by Joshua Sortino on Unsplash

TinRoll has been a work in progress. In this article we are going to set down some back-end architectural patterns. The two main things we’re going to do today is:

  1. Create a unit test project and use unit tests in our development
  2. Create a Logic layer in our app that contains our app’s logic

That’s a lot of things to do, so let’s jump in.

TinRoll Articles

This article is one of many documenting my simple stackoverflow clone called TinRoll. Other articles in this series are:

Getting Started Part 1

Getting Started Part 2

Code can be found at the Github project.

I’ve created a git tag called blog/unitTests which contains the code specific to this blog post. Make sure you’re viewing that tag if you’re trying to follow along.

New Projects

Last time we left our project we had created pages to view all questions, create a new question and view a single question.

Today let’s get started by adding two new projects. We need to add a class library called TinRoll.Logic and a xUnit Test Project called TinRoll.Test.

If you remember from previous articles our controllers accessed our TinRollContext directly.

That’s pretty bad design. Today we’re going to setup a repository that handles interaction with our database. Also a manager that will contain all of our logic and process data from the controller and the data layer. We’ll also create mappers that will handle mapping data from our Dto to our Db entity classes. Lets get started with the mappers.

Mapping Code

Lets first take a stab at our mapping code. I’m going to use unit tests to aid in my development. Using unit tests area bedrock of Test Driven Development. I’ve lately been using Unit Tests more and have found a lot of benefits from them.

What are we hoping our unit tests will help us validate? For our mapping code, that we can correctly map between our Dto and Db class. We’ll be demoing that with our User entity.

I mentioned in the last article that normally this would be the time to use AutoMapper to map our objects automatically. I’ve used AutoMapper in the past, but I’ve been thinking that the cons of AutoMapper outweigh the pros. So in TinRoll I’m trying out not using AutoMapper.

So in our Test project, create a new folder called Mapper and under that create a class called UserMapperTests.

When writing unit tests, the phrase “arrange, act, assert” is a good way to detail the steps needed for unit tests. We first arrange the necessary data to prep for the test, whether that be creating classes or mocking interfaces. We then act, run the code that is under test. Then we assert, verify that the results matched with our expectations.

Right now this code doesn’t build because UserMapper doesn’t exist. So we have a failing unit test that we are trying to code against.

In our Logic project, add a folder called Mapper and under that create a class called UserMapper. In that class we’ll write a simple Mapper from our DbEntity to our Dto.

Return to our unit test, make sure our reference to UserMapper is good and now the unit test should pass. Congratulations!

Lets do something similar for a ToDb method. Unit test:

Then write another simple mapper to map between these two objects.

Great, we now have mappings around our User objects. Now we’ll create our first repository class.

Repository

The repository pattern helps us abstract out interactions with the database. In our Data project create a new folder called Repository. Because we want to leverage dependency injection with our repository, also create a folder called Interface under our Repository folder. In our Interface folder, create an interface called IUserRepository. And define the following methods:

Now that we have our interface, lets go back to our UserRepositry, inherit the interface, pass in TinRollContext into the constructor and provide methods that throw “NotImplementedExceptiosn”.

Lets get to our unit tests now. In our Test project create a new folder called Repository, inside of that create a class called UserRepositoryTests. Our first unit test will be testing the CreateUser method. We are going to be leveraging EF Core’s in memory database to test our TinDbContext.

If we run this unit test it will fail because it throws a NotImplementedException. Lets write our UserRepository to pass the test.

This one turned out to be pretty simple,

Just add the object to the store, call save and return. Run the unit test again and you should see green!

Get User

Now lets write a test for our GetUserRepository endpoint. This unit test will be very similar to the Create User, just calling a different method.

If we run that unit test we hit the “Not Implemented Exception” on the repository. So we have a failing unit test, now lets implement it.

This one is super trivial, just return the object that is needed. Run the unit test again to see that it passes and we’re golden!

Logic Layer Tests

Now that we have unit tests for our mapper and repository, lets add our Logic Layer code to bridge the gap between the Controller and the Repository.

In our TinRoll.Logic project create a folder called Manager, within that folder create another folder called Interface.

In the Interface folder, create a file called IUserManager. Our Manager classes will sit between our Controllers and our Repositories. Managers will contain a lot of our business logic, as well as any transformations that our UI layer needs. Our first UserManager will be very thin, basically just calling our database with mapping objects before and after each call. As our app grows and more business logic is needed our Managers will start to do more things.

For our User entity, we need a way to communicate from our UI and our data layer.

With our managers, we are are finally removing our Database entity classes from our controller. As you can see the inputs and return types from the Manager are our UserDto class.

In the Manager folder create a class called UserManager, have it implement the IUserManager interface with NotImplementedExceptions being thrown.

Manager Tests

Now that we have our class, lets create unit tests before we implement our UserManager methods. This unit test is actually going to get interesting because we have to deal with our IUserRepository interface.

When this app is running, this interface will hold the real UserRepository and talk to the database. We could pass that class in with it talking to the in-memory database. But when writing unit tests it’s crucial to keep what you’re testing to a minimum. Unit tests are for testing individual pieces of code, not how code interacts between multiple layers of your app.

Plus, we already wrote unit tests for the repository. So we don’t want to test that functionality again.

In order to test our Manager without dealing with our Repository, we are going to mock our repository. Mocking allows us to granularly define what in our code is being tested. Anything we choose not to test (in this case our repository), we mock to ensure that it behaves as expected and remove that complexity from our test.

We’ll be using the Moq library to mock our class. Add a reference to the Moq nuget package. Then go back to our Unit Test and create our test.

Let me walk you through what we’re doing. When using Moq and mocking, we create and then use mock objects in our tests. We need to create the objects and methods that our unit tests needs in order to run successfully. Our IUserRepository currently has three methods, but in this particular unit test we only care about the CreateUserAsync() method. So we’ll create a mock of IUserRepository and setup a mock method for our CreateUserAsync(), specifying the object to be returned whenever that method is called.

When we create a UserManager, and instead of passing in the real UserRepository class we pass in our mock object with our mock method defined.

Then we perform our work, then assert our results. A fairly simple concept to get the hang of. When we run this we get an exception because we haven’t implemented anything yet. So lets do that.

Our implementation is very simple, just using the Mapper to convert the types for us, with a call to the UserRepository in the middle.

If we run our test again it passes!

Then lets add a unit test for GetUser, following much the same pattern.

Then go to UserManager and implement the necessary functionality.

The last thing we need to do is to update our UserController in order to call the UserManager instead of directly using the TinRollContext and EntityFramework directly.

Pretty self explanatory, we’re just using the Manager class to abstract away the saving and getting of Users. The slims up our controller and pleases the best practices side of me.

I’ve implemented something very similar for our Question entities. But since a lot of this stuff is so similar there’s a lot of code duplication. One of my next tasks is going to create generic versions of Repositories and Managers in order reduce the times I have to write and unit test the same code.

Conclusion

Our API has taken a significant steps in defining patterns that we’ll need moving forward. We’ve now introduced Repositories and Managers into our solution. We’ve also added unit testing which will help ensure our app logic is correct moving forward.

More things to come in the future. I need to actually implement some Blazor best practices, create some generic versions of objects we’ve created here, I’m also thinking about how I can get F# into this project. I’ve always been interested in it and I would like to use soon. Stay tuned.

I’m Morgan Kenyon. I’m a .NET developer working in the DFW area. I find C# a great language to use and it’s also backed by a great ecosystem, I love solving hard problems and want to continue talking about the tech I use. If you found this article helpful or thought provoking leave a comment and lets connect over LinkedIn!

Github Repo

--

--

Morgan Kenyon
The Startup

I’m a software developer who works with the Microsoft stack. I love to program and write about what I’m doing!