Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write Moq Unit Test for internal method in the same class using Autofac

I am trying mock internal method in the same class.But my mocking fails.

Here is my code.

Interface

public interface IStudentService
{
    int GetRank(int studentId);
    IList<Subject> GetSubjects(int studentId);
}

Implementation

public class StudentService : IStudentService
{
    private readonly IStudentRepository _studentRepository;
    private readonly ISubjectRepository _subjectRepository;

    public StudentService(IStudentRepository studentRepository, ISubjectRepository subjectRepository)
    {
        _studentRepository = studentRepository;
        _subjectRepository = subjectRepository;
    }

    public int GetRank(int studentId)
    {
        IList<Subject> subjects = GetSubjects(studentId);

        int rank = 0;
        //
        //Calculate Rank
        //
        return rank;
    }

    public virtual IList<Subject> GetSubjects(int studentId)
    {
        return _subjectRepository.GetAll(studentId);
    }
}

Unit Test

[TestFixture]
public class StudentServiceTest
{
    [SetUp]
    public void Setup()
    {

    }

    [TearDown]
    public void TearDown()
    {

    }

    [Test]
    public void GetRankTest()
    {
        using (var mock = AutoMock.GetStrict())
        {
            var mockStudentService = new Mock<IStudentService>();
            mockStudentService.Setup(x => x.GetSubjects(1)).Returns(new ServiceResponse<SystemUser>(new List<Subject>{ new AccounProfile(), new AccounProfile()}));
            mock.Provide(mockStudentService.Object);

            var component = mock.Create<StudentService>();
            int rank = component.GetRank(1);
            mockStudentService.VerifyAll();

            Assert.AreEqual(1, rank, "GetRank method fails");
        }
    }
}

When I debug the code it is not mocking the GetSubjects method. It actually go inside to that method. I am using Nunit, Moq and Autofac to write unit test.

Thanks In Advance!

like image 583
yohan.jayarathna Avatar asked Dec 11 '25 05:12

yohan.jayarathna


1 Answers

There are two solutions.

1. Partial mocking

In this approach you create mock of component you're testing (StudentService) and tell Moq to mock some of its methods (GetSubjects -- to-be-mocked methods must be virtual), while delegating others (GetRank) to base implementation:

Setting mock.CallBase = true instructs Moq to delegate any call not matched by explicit Setup call to its base implementation.

// mockStudentService is not needed, we use partial mock
var service = mock.Create<StudentService>();
service.CallBase = true;
service.Setup(m => m.GetSubjects(1)).Returns(...);

var rank = service.GetRank(1);
// you don't need .VerifyAll call, you didn't not set any expectations on mock
Assert.AreEqual(1, rank, "GetRank method fails");

2. Mocking internal service (ISubjectRepository)

Partial mocks are reserved for special cases. Your case is rather common one. Instead of mocking itself, your component (StudentService) could rely on mocked ISubjectRepository to provide subjects for it:

using (var mock = AutoMock.GetStrict())
{
    var subjectRepositoryMock = new Mock<ISubjectRepository>();
    subjectRepositoryMock.Setup(x => x.GetSubjects(1)).Returns(...);
    mock.Provide(subjectRepositoryMock.Object);

    var component = mock.Create<StudentService>();
    int rank = component.GetRank(1);
    // verify is not needed once again

    Assert.AreEqual(1, rank, "GetRank method fails");
}
like image 53
k.m Avatar answered Dec 12 '25 18:12

k.m



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!