Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TDD - Want to test my Service Layer with a fake Repository, but how?

I've designed an application that uses the repository pattern, and then a separate service layer such as this:

public class RegistrationService: IRegistrationService
{
    public void Register(User user)
    {
        IRepository<User> userRepository = new UserRepository();
        // add user, etc
    }
}

As you can see, I am instantiating my repository inside of the Register method. Now as I want to write some unit tests, I can't really get to it and replace it with a fake repository can I?

I don't want to add the repository as class variable though (and set it through a constructor) because I think that would make my code "smelly" (not all repositories are needed for all methods, and I don't need the calling layer to know about repositories etc.).

Suggestions?

like image 440
Alex Avatar asked Jun 01 '09 17:06

Alex


People also ask

What is the difference between service layer and repository layer?

Repository layer is implemented to access the database and helps to extend the CRUD operations on the database. Whereas a service layer consists of the business logic of the application and may use the repository layer to implement certain logic involving the database.

What is Repository Service pattern?

The Repository-Service pattern breaks up the business layer of the app into two distinct layers. The lower layer is the Repositories. These classes handle getting data into and out of our data store, with the important caveat that each Repository only works against a single Model class.

What are some examples of unit testing characteristics?

Characteristics of a good unit testFast: It isn't uncommon for mature projects to have thousands of unit tests. Unit tests should take little time to run. Milliseconds. Isolated: Unit tests are standalone, can be run in isolation, and have no dependencies on any outside factors such as a file system or database.


2 Answers

You need to use Dependency Injection. UserRepository is a dependency of your RegistrationService class. To make your classes properly unit testable (i.e. in isolation of their dependencies), you need to "invert" what controls your dependency creation. Currently, you have direct control, and are creating them internally. Just invert that control, and allow something external (such as an IoC container like Castle Windsor) inject them:

public class RegistrationService: IRegistrationService
{
    public RegistrationService(IRepository<User> userRepo)
    {
        m_userRepo = userRepo;
    }

    private IRepository<User> m_userRepo;

    public void Register(User user)
    {
        // add user, etc with m_userRepo
    }
}

I think I forgot to add. Once you allow your dependencies to be injected, you open up the possability of them being mockable. Since your RegistrationService now takes its dependent user repository as input to its constructor, you can create a mock repository and pass that in, instead of an actual repository.

like image 139
jrista Avatar answered Sep 26 '22 13:09

jrista


Dependency Injection can come in really handy for these kinds of things:

public class RegistrationService: IRegistrationService
{
    public void Register(User user)
    {
        this(user, new UserRepository());
    }

    public void Register(User user, IRepository<User> userRepository)
    {
        // add user, etc
    }

}

This way you get the ability to use UserRepository as your default, and pass in a custom (Mock or Stub for testing) IRepository for whatever else you need. You may also want to change the scoping of the 2nd register method in case you don't want to expose it to everything.

An even better alternative would be to use an IoC container which would buy you syntax that is even more decoupled than the above example. You could get something like this:

public class RegistrationService: IRegistrationService
{
    public void Register(User user)
    {
        this(user, IoC.Resolve<IRepository<User>>());
    }

    public void Register(User user, IRepository<User> userRepository)
    {
        // add user, etc
    }

}

There are many IoC containers out there, as listed by other people's answers, or you could roll your own.

like image 38
Joseph Avatar answered Sep 26 '22 13:09

Joseph