简体   繁体   English

单元测试一个工作单元

[英]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.我正在尝试对 Commit 方法进行单元测试。 I am using nunit and moq.我正在使用 nunit 和最小起订量。

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.您将插入 DBContext 的模拟,然后验证是否在提交时调用了 SaveChanges 方法。

[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.您需要模拟 DbContext,然后验证是否调用了 SaveChanges。 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.创建您的 edmx 文件,删除自定义工具,使其不会自动生成实体。

Open the edmx file, right click and add code generation item - go to online templates under database and select the EF POCO mockobject generator.打开edmx文件,右键添加代码生成项-go到数据库下的在线模板和EF POCO mockobject生成器select。 This creates two T4 templates (one for entities and another for the object context and mock object context).这将创建两个 T4 模板(一个用于实体,另一个用于 object 上下文和模拟 object 上下文)。

The one T4 template will generate your poco entities for you.一个 T4 模板将为您生成 poco 实体。 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.另一个 T4 模板将创建一个您可以扩展以用作工作单元的接口,该接口在实际 object 上下文和模拟 object 上下文中实现。 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.扩展它只需要修改 T4 模板以在生成的接口 (void SaveChanges()) 上包含一个附加方法,并在模拟 object 上下文中实现该方法。

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.您可以拥有另一个具有基本验证的 AppointmentLogic 存储库作为示例。

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!祝你好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM