Ben_jamin
Ben_jamin

Reputation: 316

Unit Testing Generic Methods (NUnit)

I have been trying to implement the repository pattern in .Net Core using a generic class. This is what i have come up with (ive taken out all but one method to keep it simple). The class / method works, but im trying to write a unit(integration) test for it, in this case the Add Method.

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

So far my integration test is as follows, Set Up makes a SqlLite database to save to Sets up the DbContext Sets up a Unit of Work - The unit of work is needed to SaveChanges the the DbContext and can be ignored for the most part

Then im creating a "StorageSystem" Domain Object and testing the Generic class. But it feels like I should be able to test it with out passing a specific domain model. Or at the very least, enter the different domain models as parameterised tests.

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

I am fairly new to working with Generics, The closest solution I could find was using an abstract class. However I couldnt make it working with me code with it not detecting the tests as an abstract class and not being able to make a repository of type 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();
}

So in short, how do i unit test this Generic Repository?

Upvotes: 1

Views: 441

Answers (1)

Manuel Fuchs
Manuel Fuchs

Reputation: 417

You could Restrict your Repository to a certain base class created by you like EntityBase (this should be abstract)

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

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

Then you are able to pass your base type to this test methods.

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

Since you are ensuring through inherintance, that every type has at least the members of EntityBase, you do not have to test further more down the inheritance tree of your entity types. (unless you have a certain use-case for that)

If you certain methods, that you need to know on the base-type, but the implementation depends on the child class, simply put the method as abstract member in the base class and override it in the 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)
   {
      ...
   }
}

Upvotes: 1

Related Questions