TDD with Monorail – Part 5

Service Layer

Currently the responsibilities of my UserService are simply are to:

  • Validate the request
  • Map to a domain object
  • Delegate the actual work to fulfil the request to a repository

These requirements are reflected in my test for the Save method in the UserService:

using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;
using TimEscott.GrassRoots.Core.Domain;
using TimEscott.GrassRoots.Core.Mappers;
using TimEscott.GrassRoots.Core.Messages.Users;
using TimEscott.GrassRoots.Core.Repositories;
using TimEscott.GrassRoots.Core.Services;

namespace TimEscott.GrassRoots.Specs.Unit.Services
{
    [TestFixture]
    public class When_saving_a_user : UnitTestBase
    {
        private MockRepository mockery;
        private IRepository<User> repository;
        private IUserMapper mapper;
        private UsersService service;

        [SetUp]
        public void SetUp()
        {
            mockery = new MockRepository();
            repository = mockery.DynamicMock<IRepository<User>>();
            mapper = mockery.DynamicMock<IUserMapper>();

            service = new UsersService(repository, mapper);
        }

        [Test]
        public void Should_validate_the_save_user_request_save_a_user_and_return_a_valid_response()
        {
            var request = mockery.DynamicMock<ISaveUserRequest>();
            var user = userBuilder.BuildVaildObject();
            SaveUserResponse response;

            using (mockery.Record())
            {
                Expect.Call(() => request.Validate());
                Expect.Call(mapper.MapFrom(request)).Return(user);
                repository.Save(user);
            }

            using (mockery.Playback())
            {
                response = service.Save(request);
            }

            Assert.That(response.Success, Is.True);
        }
    }
}

You’ll notice that I have given up with a convention that I stated using in an earlier post which was to call the system under test ‘subject’. I have chosen to give the fields names that give me some more context.

Writing this test has meant that I have needed to create a couple of other interfaces:

  • IUserMapper – will take a SaveUserRequest and return a valid User object
  • IRepository<User> – will be able to persist users

These were again mocked, to focus the test. Here is the implementation of the service:

using TimEscott.GrassRoots.Core.Domain;
using TimEscott.GrassRoots.Core.Mappers;
using TimEscott.GrassRoots.Core.Messages.Users;
using TimEscott.GrassRoots.Core.Repositories;

namespace TimEscott.GrassRoots.Core.Services
{
    public class UsersService : IUsersService
    {
        private readonly IRepository<User> repository;
        private readonly IUserMapper mapper;

        public UsersService(IRepository<User> repository, IUserMapper mapper)
        {
            this.repository = repository;
            this.mapper = mapper;
        }

        public SaveUserResponse Save(ISaveUserRequest request)
        {
            request.Validate();

            repository.Save(mapper.MapFrom(request) as User);

            return new SaveUserResponse(true);
        }
    }
}

UnitTestBase

A point of interest about the unit test above is that it extends my UnitTestBase. UnitTestBase contains a number of protected fields for building objects used in the unit tests.

Some history behind this, I have worked on projects where the objects used in the tests are created as part of the setup or as part of the test itself each time one is required, resulting in a lot of duplicated code. I think that this approach is great in some respects as it clearly defines the intent of the test. But my concern was that if I were to make a change to the structure of a domain object, for example, then the number of changes that I would have to make to my tests just to get them to compile could be wide reaching. Maybe this is what you actually want because the tests are doing their job of highlighting changes to the behaviour and specifications of the code and forcing you to consider each case.

To counter some of this I am going to experiment with having some builder objects in UnitTestBase which I instantiate in the default constructor, which I can call to create valid or invalid objects of a particular type. As I said this is an experiment so we will have to re-evaluate in a few weeks and see if we can refactor and improve on the initial idea. I can already see that the number of these objects might grow rapidly so the management of them might be an issue, but in the spirit of YAGNI until I have a problem I am not going to try and fix it. I would also rather that the builder objects were instantiated during a setup rather than once in the constructor. I am also considering getting it to create mockery for me as well – more on this when I get round to refactoring it. For reference here is the current code for the TestUnitBase and a builder class and its interface:

UnitTestBase

using TimEscott.GrassRoots.Specs.Builders.Domain;
using TimEscott.GrassRoots.Specs.Builders.Messages;

namespace TimEscott.GrassRoots.Specs.Unit
{
    public abstract class UnitTestBase
    {
        protected UserBuilder userBuilder;
        protected SaveUserRequestBuilder saveUserRequestBuilder;

        protected UnitTestBase()
        {
            userBuilder = new UserBuilder();
            saveUserRequestBuilder = new SaveUserRequestBuilder();
        }
    }
}

IBuilder – Can currently provide an Invalid and Valid object of type T.

namespace TimEscott.GrassRoots.Specs.Builders
{
    public interface IBuilder<T>
    {
        T BuildVaildObject();
        T BuildInVaildObject();
    }
}

SaveUserRequestBuilder

using TimEscott.GrassRoots.Core.Messages.Users;

namespace TimEscott.GrassRoots.Specs.Builders.Messages
{
    public class SaveUserRequestBuilder : IBuilder<SaveUserRequest>
    {
        public SaveUserRequest BuildVaildObject()
        {
            return new SaveUserRequest()
                       {
                           Email = "tim.escott@gmail.com",
                           FirstName = "Tim",
                           Surname = "Escott",
                           Phone = "09988776655"
                       };
        }

        public SaveUserRequest BuildInVaildObject()
        {
            return new SaveUserRequest()
                       {
                           Email = null,
                           FirstName = null,
                           Surname = null,
                           Phone = null
                       };
        }
    }
}

A pretty short post this time looking at the service layer and the builder idea, in part 6 we will look at the repositories and the mappers.

Advertisements

~ by Tim on 6 August 2008.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: