I am building a small TimeSheet application which helps to keep track of the employee working hours. It is a desktop application using the MVP (Model View Presenter) pattern. The DAL is driven by using the Castle Active Record Framework.
One of my views is AddEmployeeView which basically is used to create an employee account for the application. Here is the interface for the view:
public interface IAddEmployeeView
{
string FirstName { get; set; }
string LastName { get; set; }
string BarCode { get; set; }
string Message { get; set; }
}
And here is the presenter AddEmployeePresenter:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TimeSheet.Domain.Views;
using TimeSheet.Domain.Model;
using TimeSheet.Domain.Repositories;
namespace TimeSheet.Domain.Presenter
{
public class AddEmployeePresenter
{
private IAddEmployeeView _view;
public AddEmployeePresenter(IAddEmployeeView view)
{
_view = view;
}
public void AddEmployee()
{
{
BarCode = _view.BarCode,
FirstName = _view.FirstName,
LastName = _view.LastName
};
employee.Save();
if (employee.Id > 0)
{
_view.Message = "Employee inserted";
}
else { _view.Message = "Employee not inserted"; }
}
}
}
The presenter has a reference to the view and uses the view to call the methods on the model. The AddEmployee method in the presenter takes the arguments from the view and populates an Employee object. Finally, the employee.Save() is called which stores the employee into the database.
And here is the unit test for the AddEmployeeView:
[Test]
[RollBack]
public void should_get_employee_inserted_message_when_the_employee_is_inserted()
{
MockRepository mocks = new MockRepository();
var mockedView = mocks.CreateMock<IAddEmployeeView>();
Expect.Call(mockedView.BarCode).Return("1111");
Expect.Call(mockedView.FirstName).Return("Mohammad");
Expect.Call(mockedView.LastName).Return("Azam");
mockedView.Message = "Employee inserted";
mocks.ReplayAll();
AddEmployeePresenter presenter = new AddEmployeePresenter(mockedView);
presenter.AddEmployee();
mocks.VerifyAll();
}
In order to run the tests you also need to initialize the Active Record Framework. For this I have created a Test_Base class from which all the unit testing classes inherits. The base class is used to initialize the Active Record Framework.
public class Test_Base
{
static Test_Base()
{
string resourceName = "TimeSheet.Tests.appconfig.xml";
Assembly assembly = Assembly.Load("TimeSheet.Tests");
XmlConfigurationSource config = new XmlConfigurationSource(assembly.GetManifestResourceStream(resourceName));
ActiveRecordStarter.Initialize(config,typeof(Employee),typeof(TimeCard));
}
}
And the unit test class now inherits from the Test_Base class:
public class Test_AddEmployeeView : Test_Base
If you run the test it will pass. But there is a problem. The test is dependent on the database. If there is no database then the employee.Save() method will never insert the value into the database and the test will fail. We need to setup the test in such a way that it will run without using the database.
There are several ways you can handle this scenario. You can create a new class which inherits from the Employee class and overrides the Save method to set the Id = 12 (Any id number as long as it is greater than zero). But then you will need to change the AddEmployee method of the presenter. To me this is not good since you are changing the implementation of your presenter only to make your unit test successful. And once the database is created you will change it again to target a different scenario.
One approach is to introduce a new layer called Repositories. You can use Repositories to Add, Remove, Fetch etc items from the database. The repository will be an interface like IEmployeeRepository which can be mocked easily and can return expected results.
The IEmployeeRepository looks something like this:
public interface IEmployeeRepository
{
bool AddEmployee(Employee employee);
}
Now, the AddEmployee method in the AddEmployeePresenter looks something like this:
public void AddEmployee()
{
bool result = _repository.AddEmployee(
new Employee()
{
BarCode = _view.BarCode,
FirstName = _view.FirstName,
LastName = _view.LastName,
});
if (result)
{ _view.Message = "Employee inserted"; }
else { _view.Message = "Employee not inserted"; }
//employee.Save();
//if (employee.Id > 0)
//{
// _view.Message = "Employee inserted";
//}
//else { _view.Message = "Employee not inserted"; }
}
The code is not bound to any single implementation. I can pass in SqlEmployeeRepository or AccessEmployeeRepository and it will work.
You can also use View events to perform the same action. Check out the new event in the IAddEmployeeView interface.
public interface IAddEmployeeView
{
string FirstName { get; set; }
string LastName { get; set; }
string BarCode { get; set; }
string Message { get; set; }
event EventHandler EmployeeAdded;
}
The presenter AddEmployeePresenter handles the EmployeeAdded event.
public AddEmployeePresenter(IAddEmployeeView view)
{
_view = view;
// attach the events to the presenter
SetViewEvents();
}
private void SetViewEvents()
{
_view.EmployeeAdded += new EventHandler(_view_OnEmployeeAdded);
}
void _view_OnEmployeeAdded(object sender, EventArgs e)
{
EventRaised = true;
AddEmployee();
}
And finally, the unit test for the event based approach looks something like this:
[Test]
[RollBack]
public void should_get_employee_inserted_message_when_employee_is_inserted_view_events()
{
MockRepository mocks = new MockRepository();
var addEmployeeView = mocks.CreateMock<IAddEmployeeView>();
Expect.Call(addEmployeeView.BarCode).Return("1111");
Expect.Call(addEmployeeView.FirstName).Return("Mohammad");
Expect.Call(addEmployeeView.LastName).Return("Azam");
addEmployeeView.Message = "Employee inserted";
addEmployeeView.EmployeeAdded += null;
IEventRaiser onEmployeeAddedRaiser = LastCall.IgnoreArguments().GetEventRaiser();
mocks.ReplayAll();
AddEmployeePresenter presenter = new AddEmployeePresenter(addEmployeeView);
onEmployeeAddedRaiser.Raise(addEmployeeView, EventArgs.Empty);
mocks.VerifyAll();
Assert.IsTrue(presenter.EventRaised);
}
Which technique you use depends on your needs. I personally like the Presenter firing the methods approach. Since, the presenter already has a reference to the view it does not need to attach to the view events to fire appropriate methods.
If you have any suggestions then please share!