使用最小起订量模拟嵌套依赖项

本文关键字:模拟 嵌套 依赖 | 更新日期: 2023-09-27 18:32:09

我假设最小起订量会自动为任何嵌套依赖项创建模拟。

我正在对 ASP.Net MVC 控制器进行单元测试:

public class TransactionController : Controller
{
    private readonly ITransactionService _transactionService;
    private readonly SearchPanelVmBuilder _searchPanelVmBuilder;
    private readonly TransactionVmsBuilder _transactionVmsBuilder;
    public TransactionController(TransactionVmsBuilder transactionVmsBuilder, ITransactionService transactionService, SearchPanelVmBuilder searchPanelVmBuilder)
    {
        _transactionVmsBuilder = transactionVmsBuilder;
        _transactionService = transactionService;
        _searchPanelVmBuilder = searchPanelVmBuilder;
    }
    // other methods omitted for brevity
    public PartialViewResult SearchPanel()
    {
        var vm = _searchPanelVmBuilder.BuildVm();
        return PartialView("_SearchPanel", vm);
    }
}

单元测试代码:

[Fact]
public void SeachPanel_Calls_BuildSearchPanelVm()
{
    // Arrange
    var mockTransService = new Mock<ITransactionService>();
    var mockTransVmsBuilder = new Mock<TransactionVmsBuilder>();
    var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>();
    var controller = new TransactionController(mockTransVmsBuilder.Object, mockTransService.Object, mockSearchPanelVmBuilder.Object);
    // Act
    controller.SearchPanel();
    // Assert
    mockSearchPanelVmBuilder.Verify(x => x.BuildVm());
}

最小起订量投诉:

无法实例化类的代理:MCIP。Web.UI.ViewModelBuilders.Singular.SearchPanelVmBuilder. 找不到无参数构造函数。

它无法实例化代理的类:

public class SearchPanelVmBuilder
{
    private readonly ITransactionTypeService _transactionTypeService;
    private readonly TransactionTypeVmBuilder _transactionTypeVmBuilder;
    private readonly UserProvider _userProvider;
    public SearchPanelVmBuilder(
        UserProvider userProvider,
        ITransactionTypeService transactionTypeService,
        TransactionTypeVmBuilder transactionTypeVmBuilder
        )
    {
        _userProvider = userProvider;
        _transactionTypeService = transactionTypeService;
        _transactionTypeVmBuilder = transactionTypeVmBuilder;
    }
    public virtual SearchPanelVm BuildVm()
    {
        return new SearchPanelVm
        {
            Userlist = _userProvider.GetOperators(),
            TransactionTypes =
                _transactionTypeService.GetAll().Select(x => _transactionTypeVmBuilder.BuildVmFromModel(x)).ToList()
        };
    }
}

其对应的依赖关系:

public class UserProvider
{
    private static int retryCount;
    public virtual List<string> GetOperators()...
    public virtual List<string> GetGroupsForUser(WindowsIdentity identity)...
}
public interface ITransactionTypeService
{
    List<TransactionType> GetAll();
}
public class TransactionTypeVmBuilder
{
    public virtual TransactionTypeVm BuildVmFromModel(TransactionType transactionType)...
}

我做错了什么吗?

我是否必须明确告诉最小起订量才能尝试自动模拟嵌套依赖项?

还是我必须显式设置嵌套的模拟 - 如下所示:

var mockUserProvider = new Mock<UserProvider>();
var mockTransTypeService = new Mock<ITransactionTypeService>();
var mockTransactionTypeVmBuilder = new Mock<TransactionTypeVmBuilder>();
var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>(mockUserProvider.Object, mockTransTypeService.Object, mockTransactionTypeVmBuilder.Object);

使用最小起订量模拟嵌套依赖项

是的,你的假设是正确的。由于类SearchPanelVmBuilder没有提供无参数构造函数,因此无法像这样创建此类的 Mock:

var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>()

导致异常:Could not find a parameterless constructor


而是通过提供所有参数来创建 Mock,如下所示:

UserProvider userProvider = new UserProvider();
Mock<ITransactionTypeService> transactionTypeService = new Mock<ITransactionTypeService>();
TransactionTypeVmBuilder transactionTypeVmBuilder = new TransactionTypeVmBuilder();
// Use constructor with parameters here because SearchPanelVmBuilder 
// doesn't have parameterless constructor
var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>(
    userProvider, transactionTypeService.Object, transactionTypeVmBuilder);
var mockTransService = new Mock<ITransactionService>();
var mockTransVmsBuilder = new Mock<TransactionVmsBuilder>();
var controller = new TransactionController(
    mockTransVmsBuilder.Object, 
    mockTransService.Object, 
    mockSearchPanelVmBuilder.Object);

然后可以创建控制器TransactionController的实例,并可以在其上调用方法SearchPanel

在这种情况下,您只想测试调用 BuildVM 并返回结果,这样您就不需要模拟内部依赖项。 在调用 SearchPanel 方法之前,需要在"排列"部分中设置 BuildVM 方法的返回值。

mockSearchPanelVmBuilder.Setup(x => x.BuildVM()).Returns(mockSearchPanelVM);

因为如果不设置返回值,您将模拟类和虚拟方法,因此将运行实际实现。 使用接口时,将抛出一个错误,指示您应该设置返回值。