如何在对象中模拟复杂的嵌套成员或保留为单独的变量?

How to Mock Complicated Nested Member in Object or Keep as separate Variable?

我写信是为了用复杂的嵌套计算来测试一个复杂的 class。 我们正在查看 ActionMethod 及其 returnType。 returnType 是一个复杂的方程式,我该如何模拟它?

var methodInfoMock  = new Mock<MethodInfo>();
var actionModel = new ActionModel(methodInfoMock.Object, new List<object>(){});

我们知道如何模拟 ActionModel,但不知道它的 returnType。 所以我们把它作为自己的变量。

如果我们不知道如何模拟一个复杂的计算,最好只保留为自己的变量,还是 Class 的成员?

   public void AddProducesResponseTypeAttribute(ActionModel action, Type returnType, int statusCodeResult)
   {
        if (returnType != null)
        {
            action.Filters.Add(new ProducesResponseTypeAttribute(returnType, statusCodeResult));
        }
        else if (returnType == null)
        {
            action.Filters.Add(new ProducesResponseTypeAttribute(statusCodeResult));
            }
        }
   }

请参阅下面的 returnType 等式,

foreach (ActionModel action in controller.Actions)
{
     Type returnType = null;
     if (action.ActionMethod.ReturnType.GenericTypeArguments.Any())
     {
         if (action.ActionMethod.ReturnType.GenericTypeArguments[0].GetGenericArguments().Any())
         {
              returnType = action.ActionMethod.ReturnType.GenericTypeArguments[0].GetGenericArguments()[0]

;

无论如何,我们已经进行了测试,只是 returnType 自己挂在那里。

最终结果:

[Theory]
[InlineData(200, typeof(IActionResult))]
[InlineData(500, typeof(IActionResult))]
public void TestAddProducesResponseType(int expectedStatusCode, Type returnType)
 {
       // Arrange
        var provider = new ProduceResponseTypeModelProvider();
        var methodInfoMock = new Mock<MethodInfo>();
        var actionModel = new ActionModel(methodInfoMock.Object, new List<object>() { });

       // Act
       provider.AddProducesResponseTypeAttribute(actionModel, returnType, expectedStatusCode);

       // Assert
       actionModel.Filters.ShouldContain(new ProducesResponseTypeAttribute(returnType, expectedStatusCode));
 }

我想我不完全理解你的具体测试场景(特别是因为你似乎没有在你的测试中的任何地方验证 returnType),但是因为在评论中你邀请了更一般的答案,我将概述一些您可以应用于具有复杂内部结构(您的似乎是)的单元测试对象的方法。

  1. 确定要单元测试的功能并模拟其他所有功能。 Moq 允许您设置行为和 return 类型的模拟对象,如下所示:
    // Arrange
    var provider = new ProduceResponseTypeModelProvider();
    var methodInfoMock = new Mock<MethodInfo>();
    var yourKnownType = typeof(int);
    methodInfoMock.Setup(m => m.ReturnType).Returns(yourKnownType).Verifiable(); // you mock the actual property
    methodInfoMock.Setup(m => m.Filters.Add(It.IsAny<ProducesResponseTypeAttribute>())).Verifiable(); // with .Verifiable() Moq will make a note of calls to action.Filters.Add()
    var actionModel = new ActionModel(methodInfoMock.Object, new List<object>() { });

    // Act

    // Assert
    methodInfoMock.Verify(m => m.Filters.Add(It.Is<ProducesResponseTypeAttribute>(x => x.HasReturnType)), Times.Once); // checks if action.Filters.Add has been called with an instance of ProducesResponseTypeAttribute that had a returnType (i made the check up but hopefully you get the gist)
    methodInfoMock.Verify(m => m.Filters.Add(It.Is<ProducesEmptyResponseTypeAttribute>(x => x.HasReturnType)), Times.Never); // suppose ProducesEmptyResponseTypeAttribute inherits from ProducesResponseTypeAttribute and can also be passed to your mock. check it here 
    methodInfoMock.Verify(m => m.ReturnType, Times.Exactly(2)); // suppose you're expecting it to have been called twice

然后您可以验证是否已调用特定模拟并检查 types/values 调用。

  1. 如果内部状态过于强大且无法模拟(因为,比如说,你的行为取决于它),你可以选择将一些内部计算卸载到你自己实例化的实际对象实例。开箱即用的 Moq 不允许您这样做,但可以子类化 Mock 并使其包装实例,并调用您没有 Setup 的所有方法的实际实现。在我的 中可以找到其中的一个示例。我怀疑这可能适用于你的情况,如果你想模拟出 ActionModel 的一些位,同时通常坚持它的实现。