Универсальные методы модульного тестирования (NUnit) - PullRequest
0 голосов
/ 30 сентября 2018

Я пытался реализовать шаблон репозитория в .Net Core, используя универсальный класс.Это то, что я придумал (я взял все, кроме одного метода, чтобы сделать его простым).Класс / метод работает, но я пытаюсь написать для него модульный (интеграционный) тест, в данном случае метод Add.

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly DbContext Context;

    public Repository(DbContext context)
    {
        Context = context;
    }

     public void Add(TEntity entity)
    {
        Context.Set<TEntity>().Add(entity);
    }
}

Пока мой интеграционный тест выглядит следующим образом, Set Up создает SqlLiteбаза данных для сохранения. Установка DbContext. Установка единицы работы. Единица работы необходима для SaveChanges DbContext и может быть проигнорирована по большей части

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

[TestFixture]
public class RepositoryTests
{
    SqliteConnection _connection;
    DbContextOptions<ApplicationDbContext> _options;
    ApplicationDbContext _context;
    UnitOfWork _uow;

    [SetUp]
    public void SetUp()
    {
        _connection = new SqliteConnection("DataSource=:memory:");
        _connection.Open();

        _options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseSqlite(_connection)
            .Options;

        using (var context = new ApplicationDbContext(_options))
        {
            context.Database.EnsureCreated();
        }

        _context = new ApplicationDbContext(_options);
        _uow = new UnitOfWork(_context);
    }

    [TearDown]
    public void TearDown()
    {
        _connection.Close();
    }

    [Test]
    public void Add_AddsEntityToRepository()
    {
        //arrange
        var storageSystem = new StorageSystem {Id = 1, Name = "Storage1"};
        var repo = new Repository<StorageSystem>(_context);

        //act
        repo.Add(storageSystem);
        _uow.Complete();

        //assert
        Assert.AreEqual(1, _context.StorageSystems.Count());
    }

Я довольно новичок в работе с Generics. Наиболее близким решением, которое я смог найти, было использование абстрактного класса.Однако я не мог заставить его работать со своим кодом, так как он не обнаруживал тесты как абстрактный класс и не мог создать хранилище типа TEntity.

Пример взят здесь

[TestFixture]
public abstract class RepositoryTests1<TEntity>
{
    SqliteConnection _connection;
    DbContextOptions<ApplicationDbContext> _options;
    ApplicationDbContext _context;
    UnitOfWork _uow;

    [SetUp]
    public void SetUp()
    {
        _connection = new SqliteConnection("DataSource=:memory:");
        _connection.Open();

        _options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseSqlite(_connection)
            .Options;

        using (var context = new ApplicationDbContext(_options))
        {
            context.Database.EnsureCreated();
        }

        _context = new ApplicationDbContext(_options);

        _uow = new UnitOfWork(_context);
    }

    [TearDown]
    public void TearDown()
    {
        _connection.Close();
    }

[Test]
public void Add_AddsEntityToRepository_GenericAttempt()
    {
        //arrange
        TEntity entityToAdd = this.CreateEntity();

        var repo = new Repository<TEntity>(_context); //ERROR HERE - TEntity must be a reference type

        //act
        repo.Add(entityToAdd);
        _uow.Complete();

        //assert
        //NO IDEA WHAT THE ASSERTION WOULD BE
    }

    protected abstract TEntity CreateEntity();
}

Короче говоря, как мне выполнить модульное тестирование этого универсального репозитория?

1 Ответ

0 голосов
/ 30 сентября 2018

Вы можете ограничить свой репозиторий определенным базовым классом, созданным вами, например, EntityBase (это должно быть абстрактным)

public class EntityBase
{
    public int Id { get; set; }
}

public class Repository<TEntity> : IRepository<TEntity> where TEntity : EntityBase
{
    ...
}

Затем вы сможете передать свой базовый тип этим методам тестирования.

[Test]
public void Add_AddsEntityToRepository()
{
    //arrange
    var storageSystem = new StorageSystem {Id = 1, Name = "Storage1"};
    var repo = new Repository<EntityBase>(_context);

    //act
    repo.Add(storageSystem);
    _uow.Complete();

    //assert
    Assert.AreEqual(1, _context.StorageSystems.Count());
}

Поскольку с помощью наследования вы гарантируете, что каждый тип имеет по крайней мере членов EntityBase, вам не нужно дополнительно тестировать дерево наследования ваших типов сущностей.(если у вас нет определенного варианта использования для этого)

Если у вас есть определенные методы, которые вам нужно знать о базовом типе, но реализация зависит от дочернего класса, просто поместите метод как абстрактный членв базовом классе и переопределите его в дочернем классе.

public class EntityBase
{
    public int Id { get; set; }

    // sample method that copies member values from other object to current instance
    public abstract void CopyProperties(EntityBase other);
}

public class Student : EntityBase
{
   public int Id { get; set; }

   public override void CopyProperties(EntityBase other)
   {
      ...
   }
}
...