Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test for methods with disposable

I have a problem with unit testing. I have a standart Reprository and UnitOfWork pattern. For example, I have a UnitOfWork class:

public class UnitOfWork : IDisposable
{
    private readonly MyDbContext _context;

    ... repositories 
    private IMyEntityRepository _myEntityRepository;
    ...
}

UnitOfWok used in another part of program witch make some special operations with entities. For example, there is a method, whitch use UnitOfWork:

public IEnumerable<MyClass> MyMethod()
  {
     using (_unitOfWork = new UnitOfWork())
     {
        var myEntities= _unitOfWork.MyEntityRepository.Get();

        var result = ... some logic to convert myEntities collection to IEnumerable<MyClass> 
        return result;
     }
  }

My question is how to write unit tests for MyMethod if there is consruction using(_unitOfWork = new UnitOfWork)? How could I use fake UnitOfWork with Fake context in that case? Thanks for any advice.

like image 503
user3609841 Avatar asked Mar 12 '26 20:03

user3609841


2 Answers

To make your classes more fake-able and testable I would suggest abstracting your UnitOfWork and Repositories if possible and then use a factory to inject them into the classes that depend on them.

public interface IUnitOfWork : IDisposable {
    ... repositories 
    IMyEntityRepository MyEntityRepository;
    ...
}

And your UnitOfWork will derive from that interface

public class UnitOfWork : IUnitOfWork {...}

IUnitOfWorkFactory

public interface IUnitOfWorkFactory {
    IUnitOfWork Create();
}

With that, a dependent class can then look like this

public class MyDependentClass {
    private readonly IUnitOfWorkFactory unitOfWorkFactory;

    public MyDependentClass (IUnitOfWorkFactory unitOfWorkFactory) {
        this.unitOfWorkFactory = unitOfWorkFactory;
    }

    public IEnumerable<MyClass> MyMethod() {
         using (var _unitOfWork = unitOfWorkFactory.Create()) {
             var myEntities= _unitOfWork.MyEntityRepository.Get();

             var result = ... some logic to convert myEntities collection to IEnumerable<MyClass> 
             return result;
         }
    }
}

Now you can mock/fake your UnitOfWork and Repositories with no need to fake the Context.

Lets say you want to test/verify that the UOW is actually disposed of after calling MyMethod

(I'm using Moq and FluentAssert for demonstrative purposes)

You can construct a test as follows:

[TestMethod]
public void UOW_Should_Be_Disposed() {
    //Assert
    var fake_entities = Enumerable.Range(1, 10).Select(i => new MyEntity());
    var mockRepository = new Mock<IMyEntityRepository>();
    mockRepository.Setup(m => m.Get()).Returns(fake_entities);
    var mockUOW = new Mock<IUnitOfWork>();
    mockUOW.Setup(m => m.MyEntityRepository).Returns(mockRepository.Object);
    var mockFactory = new Mock<IUnitOfWorkFactory>();
    mockFactory.Setup(m => m.Create()).Returns(mockUOW.Object);

    //Act
    var sut = new MyDependentClass(mockFactory.Object);
    var output = sut.MyMethod().ToList();

    //Assert
    output.Should().NotBeNull();
    output.Should().HaveCount(10);
    output.Should().ContainItemsAssignableTo<MyClass>();
    mockUOW.Verify(m => m.Dispose());
}

The above shows how you can test everything easily using the frameworks mentioned.

Hope this helps

like image 125
Nkosi Avatar answered Mar 14 '26 08:03

Nkosi


You have to inject a factory of UnitOfWork into the class that contains the MyMethod method via constructor injection like this:

public class MyClass
{
    private readonly Func<UnitOfWork> unitOfWorkFactory;

    public MyClass(Func<UnitOfWork> unitOfWorkFactory)
    {
        this.unitOfWorkFactory = unitOfWorkFactory;
    }

    public IEnumerable<MyClass> MyMethod()
    {
        using (unitOfWork = unitOfWorkFactory())
        {
            //..
        }
    }
}

Please note that the class takes a Func<UnitOfWork> instead of a UnitOfWork because I am assuming that you want each call to MyMethod to have a new instance of UnitOfWork.

In your tests, you create a fake UnitOfWork and then you can pass it to the MyClass instance like this:

var sut = new MyClass(() => fakeInstance);

You also need to make sure that UnitOfWork is fakeable. For example, since it is a concrete class, you need to make sure that the relevant methods are virtual. Another approach is to have an interface IUnitOfWork that UnitOfWork implements and that MyClass uses.

like image 24
Yacoub Massad Avatar answered Mar 14 '26 09:03

Yacoub Massad



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!