开发者

unit testing a unit of work

new to unit testing. I have a unit of work that I am trying to unit test. I am probably missing something simple here. I am trying to unit test the Commit method. I am usin开发者_运维百科g nunit and moq.

public class  UnitOfWork : IUnitOfWork
{
    private readonly DbContext _context;
    public UnitOfWork(DbContext ctx)
    {
        _context = ctx;
    }

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

What do I need to do to test this?


You would insert a mock of the DBContext and then verify that the SaveChanges method is called on commit.

[Test]
public void Will_call_save_changes() {

  var mockContext = new Mock<DBContext>();
  var unitOfWork = new UnitOfWork(mockContext.Object);

  unitOfWork.Commit();


  mockContext.Verify(x => x.SaveChanges());

}


You'll need to mock the DbContext, and then verify that SaveChanges was called. Something like Moq can help you here.


That's one way of doing it.

An alternative I've come across is:

Create your edmx file, remove the custom tool so it doesn't autogenerate the entities.

Open the edmx file, right click and add code generation item - go to online templates under database and select the EF POCO mockobject generator. This creates two T4 templates (one for entities and another for the object context and mock object context).

The one T4 template will generate your poco entities for you. The other T4 template will create an interface you can extend to be used as a unit of work which is implemented in an actual object context and a mock object context. Extending it just requires you modify the T4 template to include an additional method on the generated interface (void SaveChanges()) and the implementation of that method on the mock object context.

I've found it to work very well.

Albeit for unit testing purposes, you wouldn't want to test your unit of work (unless verifying certain objects are added/deleted etc.). You would instead test repositories with predefined responsibilities - usually defined within context (ex. patient appointments).

You'd do something like this:

public class PatientAppointmentRepository : IPatientAppointmentRepository
{
    //Injected via IOC in constructor
    private readonly IUnitOfWork _unitOfWork;
    private readonly IPatientAppointmentLogic _patientAppointmentLogic;
    public void CreateAppointment(PatientAppointmentModel model)
    {
        var appointment = ModelMapper.Instance.To<PatientAppointment>(model);

        var appointmentAdded = _patientAppointmentLogic.Add(appointment);

        if(appointmentAdded)
            _unitOfWork.SaveChanges();
    }
}

public class PatientAppointmentLogic : IPatientAppointmentLogic
{
    private readonly IUnitOfWork _unitOfWork; //Set via constructor
    private readonly PatientLogic _patientLogic;
    public bool Validate(PatientAppointment appointment)
    {
        if(appointment == null)
            throw new ArgumentNullException("appointment");

        //perform some logic here
        return true;
    }
    public void Add(PatientAppointment appointment)
    {
        if(appointment == null)
            throw new ArgumentNullException("appointment");

        if(!Validate(appointment)) return; //Or throw an exception, up to you

        var patient = _patientLogic.GetById(appointment.PatientId);

        if(patient == null) return;

        patient.PatientAppointments.Add(appointment);
    }
}

It's really up to your to structure it appropiately. You could have another AppointmentLogic repository that has a base validation as an example.

Ideally, generic validation should not depend on external resources (such as a database).

You should be able to create a validation context in one swoop that would be used in further validation (first valid 'cheaply' before you validate 'expensively').

Sometimes all the 'values' you need for the validation is inside an entity you would need anyway, then use that as the validation context.

Best of luck!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜