The nest step in building my Budget application was ensuring that it could be unit tested. In my googling I came to realise the my design could be improved somewhat. I have now introduced the repository pattern with the interface looking like this...
IRepository.cs
public interface IRepository<T> : IDisposable { IQueryable<T> Query(); void Add( T entity ); void Attach( T entity ); void Remove( T entity ); void SubmitChanges(); }
and its implementation looking like this...
BudgetRepository.cs
public class BudgetRepository<T> : IDisposable, IRepository where T : class { private BudgetDataContext context; public BudgetRepository( BudgetDataContext ctxt ) { context = ctxt; } public IQueryable<T> Query() { return context.GetTable<T>(); } public void Add( T entity ) { context.GetTable<T>().InsertOnSubmit( entity ); } public void Attach( T entity ) { context.GetTable<T>().Attach( entity ); } public void Remove( T entity ) { context.GetTable<T>().DeleteOnSubmit( entity ); } public void SubmitChanges() { context.SubmitChanges(); } public void Dispose() { context.Dispose(); } }
The repository is obviously providing access to an individual table via the generic type used. Now all that is needed is to write some services to utilise the repository. The first service to be written was an IncomeDataService.
IncomeDataService.cs
public class IncomeDataService { private IRepository<Income> incomeRepository; public IncomeDataService( IRepository<Income> repository ) { incomeRepository = repository; } public IList<Income> GetAll() { IList<Income> incomes; using ( incomeRepository ) { incomes = ( from i in incomeRepository.Query() orderby i.Description select i ).ToList(); } return incomes; } public Income GetById( int ID ) { Income income; using ( incomeRepository ) { income = incomeRepository.Query().Where( i => i.IncomeId == ID ).FirstOrDefault(); } return income; } public void Save( Income entity ) { using ( incomeRepository ) { if ( entity.IncomeId > 0 ) { incomeRepository.Attach( entity ); } else { incomeRepository.Add( entity ); } incomeRepository.SubmitChanges(); } } }
Unit Testing with Rhino Mocks
Before continuing with all my services, I really want to be able to Unit Test my services to for a number of reasons. Firstly, if any potential employer views any of these posts then I can demonstrate good practises and secondly unit testing is a sure way to ensure that you code is doing what it should be doing.
I actually had a little bit of difficulty mocking my DataService but I finally got it out. I'd read a number of blogs on mocking the IQueryable
[TestMethod()] public void GetIncomeByIdTest() { // setup test collection IList<Income> incomeList = new List<Income>(); Income expectedIncome = new Income() { IncomeId = 1 }; Income unexpectedIncome = new Income() { IncomeId = 2 }; incomeList.Add( expectedIncome ); incomeList.Add( unexpectedIncome ); MockRepository mocks = new MockRepository(); IRepository repository = mocks.StrictMock<IRepository<Income>>(); IncomeDataService svc = new IncomeDataService( repository ); int actualID = 1; using ( mocks.Record() ) { repository.Expect( repo => repo.Query() ).Return( incomeList.AsQueryable<Income>() ); repository.Expect( repo => repo.Dispose() ); } using ( mocks.Playback() ) { Income actualIncome = svc.GetById( actualID ); Assert.AreEqual( expectedIncome.IncomeId, actualIncome.IncomeId ); } }
So what is actually going on here?
This unit test is is aimed at testing the IncomeDataService class. The issue we face is that it requires an instance of a repository, but we obviously don't want to use the database for testing, hence we use Rhino Mocks to create a mock object of a repository.
We use the the statement using ( mocks.Record() ) to indicate what we expect to occur when the service calls the method GetById(...). So what do we expect to occur? That the repository will return the incomeList collection as an IQueryable
using ( incomeRepository ) { income = incomeRepository.Query().Where( i => i.IncomeId == ID ).FirstOrDefault(); }when the incomeRepository.Query() is called Rhino Mocks ensures that the List of Income objects setup at the beginning of the test are returned. Likewise when we exit the using statement the Dispose() method is called next.
Hope this helps!
Enjoy
No comments:
Post a Comment