Подтвердить что ты не робот

Единичное тестирование единицы работы

новый для модульного тестирования. У меня есть часть работы, которую я пытаюсь unit test. Мне, наверное, здесь что-то не хватает. Я пытаюсь unit test метод Commit. Я использую nunit и moq.

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

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

Что мне нужно сделать, чтобы проверить это?

4b9b3361

Ответ 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, щелкните правой кнопкой мыши и добавьте элемент генерации кода - перейдите в онлайн-шаблоны в базе данных и выберите генератор mockobject EF POCO. Это создает два шаблона 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, который имеет базовую проверку.

В идеале общая валидация не должна зависеть от внешних ресурсов (например, базы данных).

Вы должны иметь возможность создать контекст проверки одним махом, который будет использоваться при дальнейшей валидации (сначала действительный "дешево", прежде чем вы проверите "дорого" ).

Иногда все "значения", необходимые для проверки, находятся внутри объекта, который вам нужен в любом случае, а затем используйте это как контекст проверки.

Удачи!