C# 单元测试工作单元

C# 单元测试工作单元,c#,unit-testing,nunit,moq,C#,Unit Testing,Nunit,Moq,单元测试的新手。我有一个工作单元,我正在尝试进行单元测试。我可能错过了一些简单的东西。我正在尝试对提交方法进行单元测试。我正在使用nunit和moq public class UnitOfWork : IUnitOfWork { private readonly DbContext _context; public UnitOfWork(DbContext ctx) { _context = ctx; } public void Com

单元测试的新手。我有一个工作单元,我正在尝试进行单元测试。我可能错过了一些简单的东西。我正在尝试对提交方法进行单元测试。我正在使用nunit和moq

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

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

我需要做什么来测试这一点?

您需要模拟DbContext,然后验证是否调用了SaveChanges。类似Moq的东西可以帮助您。

您可以插入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());

}
[测试]
public void将调用保存更改(){
var mockContext=new Mock();
var unitOfWork=新的unitOfWork(mockContext.Object);
unitOfWork.Commit();
验证(x=>x.SaveChanges());
}

这是一种方法

我遇到的另一种选择是:

创建edmx文件,删除自定义工具,使其不会自动生成实体

打开edmx文件,右键单击并添加代码生成项目-转到数据库下的联机模板并选择EF POCO mockobject生成器。这将创建两个T4模板(一个用于实体,另一个用于对象上下文和模拟对象上下文)

one T4模板将为您生成poco实体。另一个T4模板将创建一个可扩展的接口,以用作在实际对象上下文和模拟对象上下文中实现的工作单元。扩展它只需要修改T4模板,以便在生成的接口(void SaveChanges())上包含一个附加方法,并在模拟对象上下文上实现该方法

我发现它工作得很好

尽管出于单元测试的目的,您不想测试您的工作单元(除非验证添加/删除了某些对象等)。相反,您应该使用预定义的职责测试存储库-通常在上下文中定义(例如患者预约)

你可以这样做:

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);
    }
}
公共类PatientPointmentRepository:iPatientAppointRepository
{
//在构造函数中通过IOC注入
私人只读i工作单元(unitof工作单元);;
专用只读iPartientAppointLogic _patientAppointLogic;
public void CreateAppointment(PatientPointmentModel)
{
var appointment=ModelMapper.Instance.To(model);
var AppointAdded=\u PatientAppointLogic.Add(约会);
如果(新增任命)
_unitOfWork.SaveChanges();
}
}
公共类PatientAppointLogic:iPatientAppointLogic
{
私有只读IUnitOfWork _unitOfWork;//通过构造函数设置
私有只读PatientLogic\u PatientLogic;
公共bool验证(预约)
{
如果(约会==null)
抛出新的异常(“约会”);
//在这里执行一些逻辑
返回true;
}
公共无效添加(任命)
{
如果(约会==null)
抛出新的异常(“约会”);
如果(!Validate(appointment))返回;//或引发异常,则由您决定
var patient=\u patientLogic.GetById(appointment.PatientId);
if(patient==null)返回;
患者。患者药膏。添加(预约);
}
}
这真的取决于你如何恰当地构建它。您可以有另一个AppointLogic存储库,以基本验证为例

理想情况下,通用验证不应依赖于外部资源(如数据库)

您应该能够一次性创建一个验证上下文,用于进一步的验证(在“昂贵地”验证之前,先“便宜地”验证)

有时,验证所需的所有“值”都位于您无论如何都需要的实体内,然后将其用作验证上下文

祝你好运