3

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!

Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
yohan.jayarathna
  • 3,423
  • 13
  • 56
  • 74
  • 1
    Why try mocking the internal method when you can easily mock the repository? Jimmy's got it right :) – Peter Lillevold Nov 03 '14 at 19:48
  • What if GetSubject method has more than one repository methods then I have to mock them all. Further if the GetSubjects method has another method from same class it I will have to mock them too. Therefore I have to mock lots of thins apart from the method I am testing. Does Autofac support for partial mokcing? – yohan.jayarathna Nov 04 '14 at 07:24
  • 2
    First off, Autofac is a DI framework and does not relate to mocking at all. In your sample here you can remove all traces of Autofac and still be able to write tests with mocking. In fact, I always write tests with as little additional frameworks as possible: new up one instance of `StudentService` and pass in two mocked repositories. And yes, when testing a piece of code you **have to** mock the external dependencies, in your case the repositories. If you feel that there is too much going on, maybe that is a sign that your service is doing too much: split it up in more granular services then! – Peter Lillevold Nov 04 '14 at 07:34
  • ...suggestion: read up some more about what mocking really is, e.g. this thread is good http://stackoverflow.com/questions/2665812/what-is-mocking – Peter Lillevold Nov 04 '14 at 07:43

3 Answers3

7

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");
}
Community
  • 1
  • 1
k.m
  • 30,794
  • 10
  • 62
  • 86
  • I made the method virtual and tried with CallBase = true but it didn't worked :( In your second solution what if GetSubject method has more than one repository methods then I have to mock them all. Further if the GetSubjects method has another method from same class it I will have to mock them too. Therefore I have to mock lots of thins apart from the method I am testing. What is the solution ? – yohan.jayarathna Nov 04 '14 at 05:50
0

This code works for. Thank you for everyone for support

[TestFixture]
public class StudentServiceTest
{
    private Mock<StudentRepository> _studentRepositoryMock;
    private Mock<SubjectRepository> _subjectRepositoryMock;
    private Mock<StudentService> _studentServiceMock;

    [SetUp]
    public void Setup()
    {
        _studentRepositoryMock = new Mock<StudentService>(MockBehavior.Strict);
        _subjectRepositoryMock = new Mock<SubjectRepository>(MockBehavior.Strict);
        _studentServiceMock = new Mock<StudentService>(_studentRepositoryMock.Object, _subjectRepositoryMock.Object);
        _studentServiceMock.CallBase = true;
    }

    [TearDown]
    public void TearDown()
    {

    }

    [Test]
    public void GetRankTest()
    {
        _studentServiceMock.Setup(x => x.GetSubjects(1)).Returns(...);

        int rank = component.GetRank(1);
        _studentServiceMock.VerifyAll();

        Assert.AreEqual(1, rank, "GetRank method fails");
    }
}   
yohan.jayarathna
  • 3,423
  • 13
  • 56
  • 74
-1

I guess your GetSubjects method must be declared virtual, otherwise it cannot be mocked.

public virtual IList<Subject> GetSubjects(int studentId)
{
   // code here
} 
pgenfer
  • 597
  • 3
  • 13