单元测试通用方法(NUnit)

时间:2018-09-29 22:48:29

标签: c# unit-testing generics nunit

我一直在尝试使用通用类在.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);
    }
}

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

然后即时创建“ StorageSystem”域对象并测试Generic类。但是感觉到我应该能够在不传递特定领域模型的情况下对其进行测试。或至少输入不同的领域模型作为参数化测试。

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

我对使用泛型刚起步,我能找到的最接近的解决方案是使用抽象类。但是,我无法使其与我的代码一起工作,因为它无法将测试检测为抽象类,也无法创建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();
}

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

1 个答案:

答案 0 :(得分:1)

您可以将存储库限制为由您自己创建的某个特定的基类,例如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)
   {
      ...
   }
}