Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to structure unitofwork / service layer / repository, so that they work with DI (Unity) and Moq for Unit Testing

I have an MVC app (EF6, SQL Server CE 4), that I recently refactored to add a UnitOfWork class and a service layer (so that I could utilise a single DbContext per request, and conduct transactions successfully).

Previously, I was using Unity to inject the repositories into the controller. My unit tests (for the controllers) were simple to setup - I just mocked each repository, and passed those into the controller constructor.

After refactoring, I now use Unity to inject the Service Layer (to the controller) and UnitOfWork (into the Service Layer). The Service Layer now instantiates each repository, by passing the UnitOfWork.DbContext to the repository's constructor.

In my Unit Tests, I am attempting to mock the UnitOfWork, and the ServiceLayer (and pass the mocked UnitOfWork object into the ServiceLayer's constructor). However, the tests fail, saying "TestFixtureSetup failed in ControllerTest".

I assume it's due to how I'm attempting to pass the UnitOfWork mock into the ServiceLayer mock, so would appreciate any guidance on how to do this correctly.

Relevant code snippets below.

UnitOfWork

public interface IUnitOfWork:IDisposable
{
    void Save();
    IDSMContext Context { get; }
}

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private IDSMContext _context;

    public UnitOfWork()
    {
       _context = new IDSMContext();
    }

    public IDSMContext Context
    {
        get {return _context;}
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Service Layer

public interface IService
{
    // Repositories
    IUserRepository Users { get; }
    IUserTeamRepository UserTeams { get; }
    IPlayerRepository Players { get; }
    IGameRepository Games { get; }
    IUserTeam_PlayerRepository UserTeamPlayers { get; }

    void Save();
}

public class Service: IService, IDisposable
{
    private IUnitOfWork _unitOfWork;
    private IUserRepository _userRepository;
    private IUserTeamRepository _userTeamRepository;
    private IPlayerRepository _playerRepository;
    private IGameRepository _gameRepository;
    private IUserTeam_PlayerRepository _userTeamPlayerRepository;

    public Service(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        initialiseRepos();
    }

    private void initialiseRepos(){
        _userRepository = _userRepository ?? new UserRepository(_unitOfWork.Context);
        _userTeamRepository = _userTeamRepository ?? new UserTeamRepository(_unitOfWork.Context);
        _playerRepository = _playerRepository ?? new PlayerRepository(_unitOfWork.Context);
        _gameRepository = _gameRepository ?? new GameRepository(_unitOfWork.Context);
        _userTeamPlayerRepository = _userTeamPlayerRepository ?? new UserTeam_PlayerRepository(_unitOfWork.Context);
    }

    public IUserRepository Users { get { return _userRepository; } }
    public IUserTeamRepository UserTeams { get { return _userTeamRepository; } }
    public IPlayerRepository Players { get { return _playerRepository; } }
    public IGameRepository Games { get { return _gameRepository; } }
    public IUserTeam_PlayerRepository UserTeamPlayers { get { return _userTeamPlayerRepository; } }

    public void Save()
    {
        _unitOfWork.Save();
    }

Unity Container Instance Setup

    Instance.RegisterType<IService, Service>(new PerThreadLifetimeManager())
            .RegisterType<IUnitOfWork, UnitOfWork>();

Controller Constructor

public GameController(IService service)
    {
        _service = service;
    }

Test Constructor

_mockUnitOfWork = new Mock<IUnitOfWork>();
_mockServiceLayer = new Mock<IService>(_mockUnitOfWork.Object); //this line fails

Test Controller Method

GameController Controller = new GameController(_mockServiceLayer.Object);
like image 804
jag Avatar asked Nov 11 '22 13:11

jag


1 Answers

If you want to test methods of GameController you just need to mock/stub the dependencies of that class. Just do this:

_mockServiceLayer = new Mock<IService>();
_controller = new GameController(_mockServiceLayer.Object);

When you are testing the Controller, you shouldn't worry about the dependencies of the service. UnitOfWork is never exposed outside your service, so don't worry about it when testing the controller. On your tests you may now setup expectations of methods called on your service, like verifying that Save was called once (If you were testing the service, then you would worry about the IService.Save calling Save on a mock of the IUnitOfWork!):

_mockServiceLayer.Verify(s=> s.Save(), Times.Once()); 

The problem you will find is that your service class is not abstracting the controller from the repositories, as your controller will get the repositories via the properties in IService and query directly the repositories. So if you want to test your controller methods, you will still need to mock the repositories, doing something like:

//Initialization before each test:
_mockUserRepo = new Mock<IUserRepository>();
//...other repositories
_mockServiceLayer = new Mock<IService>();
_mockServiceLayer.Setup(s => s.Users).Returns(_mockUserRepo.Object);
//... setup properties in IService for other repositories
_controller = new GameController(_mockServiceLayer.Object);

//In some test:
var user = new User();    
_mockUserRepo.Setup(s => s.Get(123)).Returns(user);

call some controller method and make sure returned model is "user"

This way you may find yourself configuring the expectations and data returned by a few repositories and the UnityOfWork, just for testing the methods in the Controller! Not to mention that your Controller class effectively depends on your repositories, not just on the service.

Another approach would be if your service class contains higher level methods like GetUser, CreateUser or AddUserToTeam (likely having multiple services with closely related methods). The service would then shield the controller from retrieving/sending data to the repositories and using the UnitOfWork.

That way in your tests you would only need to mock IService. For example a test for a typical "GET" action may look like:

//Arrange
var user = new User();    
_mockServiceLayer.Setup(s => s.GetUser(123)).Returns(user);

//Act
var viewResult = _controller.GetUser(123) as ViewResult;

//Assert
Assert.AreEqual(user, viewResult.Model);

Hopefully this will help clarifying things a bit!

like image 167
Daniel J.G. Avatar answered Dec 10 '22 18:12

Daniel J.G.