【问题标题】:unit testing a unit of work单元测试一个工作单元
【发布时间】:2011-09-15 00:42:07
【问题描述】:

单元测试新手。我有一个我正在尝试进行单元测试的工作单元。我可能在这里遗漏了一些简单的东西。我正在尝试对 Commit 方法进行单元测试。我正在使用 nunit 和 moq。

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

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

我需要做什么来测试这个?

【问题讨论】:

    标签: c# unit-testing nunit moq


    【解决方案1】:

    您将插入 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());
    
    }
    

    【讨论】:

      【解决方案2】:

      您需要模拟 DbContext,然后验证是否调用了 SaveChanges。像 Moq 这样的东西可以在这里为您提供帮助。

      【讨论】:

        【解决方案3】:

        这是一种方法。

        我遇到的另一种选择是:

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

        打开edmx文件,右键添加代码生成项——进入数据库下的在线模板,选择EF POCO mockobject generator。这将创建两个 T4 模板(一个用于实体,另一个用于对象上下文和模拟对象上下文)。

        一个 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);
            }
        }
        

        这完全取决于您是否适当地构建它。您可以拥有另一个具有基本验证的 AppointmentLogic 存储库作为示例。

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

        您应该能够一口气创建一个验证上下文,用于进一步验证(在验证“昂贵”之前首先是有效的“便宜”)。

        有时,验证所需的所有“值”都在您需要的实体内,然后将其用作验证上下文。

        祝你好运!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-09-20
          • 2013-05-30
          • 1970-01-01
          • 1970-01-01
          • 2012-11-14
          • 2015-07-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多