使用 Autofac 为同一类中的内部方法编写 Moq 单元测试

yoh*_*hna 2 c# nunit unit-testing moq autofac

我正在同一个类中尝试模拟内部方法。但是我的模拟失败了。

这是我的代码。

界面

public interface IStudentService
{
    int GetRank(int studentId);
    IList<Subject> GetSubjects(int studentId);
}
Run Code Online (Sandbox Code Playgroud)

执行

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

单元测试

[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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我调试代码时,它不是在模拟 GetSubjects 方法。它实际上进入了那个方法。我正在使用 Nunit、Moq 和 Autofac 来编写单元测试。

提前致谢!

k.m*_*k.m 5

有两种解决方案。

1.部分嘲讽

在这种方法中,您创建了您正在测试的组件的模拟 ( StudentService) 并告诉 Moq 模拟它的一些方法(GetSubjects--要被模拟的方法必须是虚拟的),同时将其他方法 ( GetRank)委托给基本实现

设置mock.CallBase = true指示 Moq 将任何与显式Setup调用不匹配的调用委托给其基本实现。

// 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");
Run Code Online (Sandbox Code Playgroud)

2. 模拟内部服务 ( ISubjectRepository)

部分模拟保留用于特殊情况。你的情况比较常见。您的组件 ( StudentService) 可以依赖 mockedISubjectRepository为其提供主题,而不是模拟自身:

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");
}
Run Code Online (Sandbox Code Playgroud)