Как смоделировать сложный вложенный элемент в объекте или сохранить в качестве отдельной переменной? - PullRequest
2 голосов
/ 10 января 2020

Я пишу, чтобы протестировать сложный класс со сложными вложенными вычислениями. Мы смотрим на ActionMethod и его returnType. ReturnType - сложное уравнение, как бы я его высмеял?

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

Мы знаем, как насмехаться над ActionModel, но не его returnType. Поэтому мы сохраняем его как собственную переменную.

Если мы не знаем, как смоделировать сложные вычисления, лучше ли просто оставить их в качестве собственной переменной или члена класса?

   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));
 }

1 Ответ

1 голос
/ 15 января 2020

Мне кажется, я не до конца понимаю ваш конкретный c сценарий тестирования (особенно если учесть, что вы нигде не проверяете returnType в ваших тестах), но поскольку в комментариях вы предлагаете более общие ответы, я Обрисую пару подходов, которые вы могли бы применить к объектам модульного тестирования со сложными внутренними структурами (которые кажутся вашими). ​​

  1. Определите функциональность, которую вы тестируете, и смоделируйте все остальное. Moq позволяет вам настроить поведение и возвращать типы макетированных объектов, например, так:
    // 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

Затем вы можете проверить, был ли вызван конкретный макет, и проверить типы / значения в вызове.

Если внутреннее состояние подавляющее и не может быть смоделировано (потому что, скажем, ваше поведение зависит от него), вы можете перегрузить некоторые внутренние вычисления на фактический экземпляр объекта, который вы создаете сами. Из коробки Moq не позволяет вам сделать это, но возможно создать подкласс Mock и заставить его обернуть экземпляры вызовом фактической реализации для всех методов, которые у вас не Setup. Один пример этого можно найти в моем другом ответе SO . Я подозреваю, что это может относиться к вашему случаю, если вы хотите макетировать некоторые биты ActionModel, в то же время придерживаясь его реализации.
...