AutoMocking Container
January 13, 2008When writing unit tests that involve interaction based testing, you most likely end up using one of the mocking frameworks out there. The mocking framework I'm using is Rhino Mocks. For an explanation of the differences between state based testing and interaction based testing, you can have a look at the excellent Mocks aren't Stubs from Martin Fowler. For a nice introduction on Rhino Mocks, have a look at this screen cast.
Anyway, when writing interaction based unit tests for the following code
public class CustomerService
{
private ICustomerRepository _customerRepository;
private IActiveDirectoryGateway _activeDirectory;
public CustomerService(
ICustomerRepository customerRepository,
IActiveDirectoryGateway activeDirectory)
{
_customerRepository = customerRepository;
_activeDirectoryGateway = activeDirectory;
}
public void CreateCustomer(Customer customer)
{
Boolean succeeded =
_customerRepository.Save(customer);
if(succeeded)
{
_activeDirectory.GrantAccessTo(customer);
}
}
}
you probably end up writing unit tests like this:
[TestFixture]
public class ClassicCustomerServiceTestFixture
{
private MockRepository _mockRepository;
private ICustomerRepository _customerRepository;
private IActiveDirectoryGateway _activeDirectoryGateway;
private CustomerService _customerService;
[SetUp]
public void SetUp()
{
_mockRepository = new MockRepository();
_customerRepository = _mockRepository
.DynamicMock<ICustomerRepository>();
_activeDirectoryGateway = _mockRepository
.DynamicMock<IActiveDirectoryGateway>();
_customerService =
new CustomerService(_customerRepository,
_activeDirectoryGateway);
}
[TearDown]
public void TearDown()
{
_customerService = null;
_activeDirectoryGateway = null;
_customerRepository = null;
_mockRepository = null;
}
[Test]
public void VerifyInteractionWithCustomerRepository()
{
Customer customer = new Customer();
using(_mockRepository.Record())
{
Expect.Call(_customerRepository.Save(customer))
.Return(false)
.Message("Expected Save to be called once.");
}
using(_mockRepository.Playback())
{
_customerService.CreateCustomer(customer);
}
}
[Test]
public void VerifyInteractionWithActiveDirectoryGateway()
{
Customer customer = CreateCustomer();
using(_mockRepository.Record())
{
SetupResult.For(_customerRepository.Save(null))
.IgnoreArguments()
.Return(true);
_activeDirectoryGateway.GrantAccessTo(customer);
LastCall.Message("Expected GrantAccessTo to be
called once.");
}
using(_mockRepository.Playback())
{
_customerService.CreateCustomer(customer);
}
}
private static Customer CreateCustomer()
{
return new Customer();
}
}
As you can see, there is a lot of code in the SetUp method that creates mock objects for the dependencies that are required by the subject under test ( = the CustomerService class). Besides the fact that you need to type it over and over again for every test fixture, the major disadvantage with this approach is that the unit tests are not self-containing. Reading and understanding the unit tests involves reading both the test case methods and the SetUp/TearDown methods.
The approach I've been using for the last couple of months is the AutoMocking container from Jacob Lewallen. Lets speak code shall we.
[TestFixture]
public class CustomerServiceTestFixture :
AutoMockingTestFixture<CustomerService>
{
[Test]
public void VerifyInteractionWithCustomerRepository()
{
Customer customer = new Customer();
using(MockRepository.Record())
{
Expect.Call(
MockCustomerRepository.Save(customer))
.Return(false)
.Message("Expected Save to be called once.");
}
using(MockRepository.Playback())
{
CreateSubject().CreateCustomer(customer);
}
}
[Test]
public void VerifyInteractionWithActiveDirectoryGateway()
{
Customer customer = CreateCustomer();
using(MockRepository.Record())
{
SetupResult.For(
MockCustomerRepository.Save(null))
.IgnoreArguments()
.Return(true);
MockActiveDirectoryGateway.
GrantAccessTo(customer);
LastCall.Message("Expected GrantAccessTo to be
called once.");
}
using(MockRepository.Playback())
{
CreateSubject().CreateCustomer(customer);
}
}
private ICustomerRepository MockCustomerRepository
{
get { return Mock<ICustomerRepository>(); }
}
private IActiveDirectoryGateway
MockActiveDirectoryGateway
{
get { return Mock<IActiveDirectoryGateway>(); }
}
private static Customer CreateCustomer()
{
return new Customer();
}
}
Notice how the SetUp/TearDown methods are completely gone. I've created this base class test fixture, called AutoMockingTestFixture that encapsulates the use of the AutoMocking container, like so:
public abstract class AutoMockingTestFixture<TSubject>
{
private AutoMockingContainer _autoMockingContainer;
protected AutoMockingContainer AutoMockingContainer
{
get { return _autoMockingContainer; }
}
protected MockRepository MockRepository
{
get { return _autoMockingContainer.MockRepository; }
}
protected T CreateSubject()
{
return _autoMockingContainer.Create<TSubject>();
}
protected T Mock<T>() where T : class
{
return _autoMockingContainer.Get<T>();
}
protected T Stub<T>() where T : class
{
_autoMockingContainer.Mark<T>().Stubbed();
return _autoMockingContainer.Get<T>();
}
protected virtual void SetUp()
{}
protected virtual void TearDown()
{}
[SetUp]
public void BaseSetUp()
{
_autoMockingContainer =
new AutoMockingContainer(new MockRepository());
_autoMockingContainer.Initialize();
SetUp();
CreateSubject();
}
[TearDown]
public void BaseTearDown()
{
TearDown();
_autoMockingContainer = null;
}
}
Using this base test fixture ensures that all interaction based unit tests in the derived test fixture are completely self-containing. Everything you need to know about a particular unit test is right there in the same method. Another big advantage is the fact that you can now add dependencies to the constructor of the CustomerService class without breaking any tests whatsoever.
If you're interested in using this approach, the code for the AutoMocking container can be downloaded from Ayende's Subversion repository (see rhino-testing). Under the hood, the AutoMocking container leverages Castle Windsor for doing its magic.
If you and your team want to learn more about how to write maintainable unit tests and get the most out of TDD practices, make sure to have look at our trainings and workshops or check out the books section. Feel free to reach out at info. @ principal-it .be
Jan Van Ryswyck
Thank you for visiting my blog. I’m a professional software developer since Y2K. A blogger since Y2K+5. Provider of training and coaching in XP practices. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.
Comments
Writing Maintainable
Unit Tests
Watch The Videos
Latest articles
-
Contract Tests - Parameterised Test Cases
June 28, 2023
-
Contract Tests - Abstract Test Cases
April 12, 2023
-
Contract Tests
February 1, 2023
-
The Testing Quadrant
June 15, 2022
-
Tales Of TDD: The Big Refactoring
February 2, 2022
Tags
- .NET
- ALT.NET
- ASP.NET
- Agile
- Announcement
- Architecture
- Behavior-Driven Development
- C++
- CQRS
- Clojure
- CoffeeScript
- Community
- Concurrent Programming
- Conferences
- Continuous Integration
- Core Skills
- CouchDB
- Database
- Design Patterns
- Domain-Driven Design
- Event Sourcing
- F#
- Fluent Interfaces
- Functional Programming
- Hacking
- Humor
- Java
- JavaScript
- Linux
- Microsoft
- NHibernate
- NoSQL
- Node.js
- Object-Relational Mapping
- Open Source
- Reading
- Ruby
- Software Design
- SourceControl
- Test-Driven Development
- Testing
- Tools
- Visual Studio
- Web
- Windows
Disclaimer
The opinions expressed on this blog are my own personal opinions. These do NOT represent anyone else’s view on the world in any way whatsoever.
About
Thank you for visiting my website. I’m a professional software developer since Y2K. A blogger since Y2K+5. Author of Writing Maintainable Unit Tests. Provider of training and coaching in XP practices. Curator of the Awesome Talks list. Thinking and learning about all kinds of technologies since forever.
Latest articles
Contract Tests - Parameterised Test Cases
Contract Tests - Abstract Test Cases
Contract Tests
The Testing Quadrant
Contact information
(+32) 496 38 00 82
info @ principal-it .be