单元测试通用方法 (NUnit)

Unit Testing Generic Methods (NUnit)

我一直在尝试使用通用 class 在 .Net Core 中实现存储库模式。这就是我想出的(为了保持简单,我只剩下一种方法)。 class / 方法有效,但我正在尝试为其编写单元(集成)测试,在本例中为 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);
    }
}

到目前为止我的集成测试如下, 设置使一个 SqlLite 数据库保存到 设置 DbContext 设置一个工作单元 - 工作单元需要在 DbContext 中保存更改,大部分情况下可以忽略

然后我创建一个 "StorageSystem" 域对象并测试通用 class。但感觉我应该能够在不通过特定域模型的情况下对其进行测试。或者至少,输入不同的域模型作为参数化测试。

[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());
    }

我对使用泛型还很陌生,我能找到的最接近的解决方案是使用抽象 class。但是我无法让它与我的代码一起工作,因为它没有将测试检测为抽象 class 并且无法创建 TEntity 类型的存储库。

Example taken from here

[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();
}

简而言之,我如何对这个通用存储库进行单元测试?

您可以将您的存储库限制为您创建的某个基础 class,例如 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 的成员,因此您不必在实体类型的继承树下进一步测试。 (除非你有一定的use-case)

如果你有某些方法,你需要知道base-type,但实现取决于child class,只需将方法作为抽象成员放在基类中class 并在 child class.

中覆盖它
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)
   {
      ...
   }
}