Как я могу создавать и заполнять мои макеты с помощью Autofixture?

В настоящее время я использую EF6 для реализации своих репозиториев внутри UnitOfWork. Я также создал макетные реализации In-Memory (MockUnitOfWork и MockRepository), чтобы я мог использовать их в модульных тестах, однако теперь мне приходится иметь дело с утомительной настройкой объектов.

Разве это не то, что предназначена Autofixture? Как я могу получить MockUnitOfWork, который я могу использовать в своих тестах, в котором хранятся репозитории Foo и Barr? Я использую NSubstitute для моей насмешличной структуры.

IUnitOfWork

public interface IUnitOfWork
{
    void Save();
    void Commit();
    void Rollback();

    IRepository<Foo> FooRepository { get; }
    IRepository<Bar> BarRepository { get; }
}

IRepository

public interface IRepository<TEntity> where TEntity : class
{
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string         includeProperties = "");

    IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null);
    TEntity GetByID(object id);

    void Insert(TEntity entity);
    void Delete(object id);
    void Delete(TEntity entityToDelete);
    void Update(TEntity entityToUpdate);
}

Ответы

Ответ 1

Здесь вы пытаетесь выполнить функциональное тестирование, поэтому было бы разумно иметь функциональную базу данных.

EF может воссоздать и уничтожить вашу базу данных в методах настройки и удаления с помощью тестовой строки подключения. Это обеспечит реальную функциональную среду тестирования для ваших тестов, чтобы работать против имитации реальной среды.

Пример:

        [TestFixtureSetUp]
        public static void SetupFixture() //create database
        {
            using (var context = new XEntities())
            {
                context.Setup();
            }
        }

        [TestFixtureTearDown]
        public void TearDown() //drop database
        {
            using (var context = new XEntities())
            {
                context.Database.Delete();
            }
        }

        [SetUp]
        public void Setup() //Clear entities before each test so they are independent
        {
            using (var context = new XEntities())
            {
                foreach (var tableRow in context.Table)
                {
                    context.Table.Remove(tableRow);
                }
                context.SaveChanges();
            }
        }

Ответ 2

Да, это именно то, что он должен был сделать. См. Пример ниже. Я использую Mock вместо NSubstitute, потому что я не знаком с NSubstitute. Вам просто нужно пройти еще одну настройку и использовать синтаксис NSubstitute в настройках.

[SetUp]
public  void SetUp()
{
    // this will make AutoFixture create mocks automatically for all dependencies
    _fixture = new Fixture()
         .Customize(new AutoMoqCustomization()); 

    // whenever AutoFixture needs IUnitOfWork it will use the same  mock object
    // (something like a singleton scope in IOC container)
    _fixture.Freeze<Mock<IUnitOfWork>>(); 

    // suppose YourSystemUnderTest takes IUnitOfWork as dependency,
    // it'll get the one frozen the line above
    _sut = _fixture.Create<YourSystemUnderTest>(); 
}

[Test]
public void SomeTest()
{
    var id = _fixture.Create<object>(); // some random id
    var fooObject = _fixture.Create<Foo>(); // the object repository should return for id

    // setuping THE SAME mock object that wa passed to _sut in SetUp.
    // _fixture.Freeze<Mock part is ESSENTIAL
    // _fixture.Freeze<Mock<IUnitOfWork>>() returns the mock object, so whatever comes
    // next is Mock specific and you'll have to use NSubstitute syntax instead
    _fixture.Freeze<Mock<IUnitOfWork>>()
            .Setup(uow => uow.FooRepository.GetById(id))
            .Returns(fooObject); 

    // if this method will ask the unit of work for FooRepository.GetById(id)
    // it will get fooObject.
    var whatever = _sut.SomeMethod(id); 

    // do assertions
}

Прекрасная вещь в AutoFixture заключается в том, что вам не нужно создавать mocks для всех зависимостей вашей тестируемой системы. Если вы тестируете функциональность, которая использует только одну зависимость, вы просто заморозите ее до, создавая тестируемую систему. Остальные зависимости будут издеваться автоматически.