Как издеваться над BulkImportResponse

У меня есть метод, который вызывает Microsoft.Azure.CosmosDB.BulkExecutor.BulkImport для вставки пакета документов. Я пишу модульные тесты для этого метода. Я пробовал издеваться над методом BulkImport с помощью Moq, но не могу создать имитируемую версию BulkImportResponse. Moq не может создавать макеты, поскольку BulkImportResponse является запечатанным классом, а также все свойства этого класса доступны только для чтения, и, следовательно, я не могу установить свойства в соответствии с моим требованием во время насмешки. Может ли кто-нибудь подсказать мне, как решить эту проблему?

У меня есть логика, написанная в моем методе для тестирования,

BulkImportResponse response = await bulkExecutorWrapper.BulkImport(documents, enableUpsert);
if (response .NumberOfDocumentsImported != documents.Count)
{
   //Do some  logic 
}

И в модульном тесте я пытаюсь издеваться над этим,

bulkExecutorWrapper.Setup(bulkExecutorWrapper=> bulkExecutorWrapper.BulkImport(It.IsAny<List<object>>(), It.IsAny<bool>())).ReturnsAsync(new BulkImportResponse(){NumberOfDocumentsImported = 0});

person DevMJ    schedule 18.07.2019    source источник
comment
Невозможно высмеять свойства BulkImportResponse, поскольку они _2 _..., а сам class sealed, как вы уже заметили.   -  person Johnny    schedule 18.07.2019
comment
Есть ли способ написать для этого же модульные тесты?   -  person DevMJ    schedule 18.07.2019
comment
Не при таком подходе. Вы можете обернуть bulkExecutorWrapper своим пользовательским интерфейсом и вернуть какую-то оболочку вокруг ответа, чтобы проверить его ...   -  person Johnny    schedule 18.07.2019


Ответы (1)


У меня была такая же проблема, и я нашел решение для установки внутренних или частных свойств. Я использовал решение, которое вы можете найти на https://wrightfully.com/mocking-objects-with-restricted-access-members. Установка свойства только для чтения возможна с помощью отражения.

Решение довольно простое

Создайте статический класс MockingHelper со следующим кодом

public static void SetPropertyValue(object target, string memberName, object newValue)
{
    PropertyInfo prop = GetPropertyReference(target.GetType(), memberName);
    prop.SetValue(target, newValue, null);
}

private static PropertyInfo GetPropertyReference(Type targetType, string memberName)
{
    PropertyInfo propInfo = targetType.GetProperty(memberName,
                                          BindingFlags.Public |
                                          BindingFlags.NonPublic |
                                          BindingFlags.Instance);

    if (propInfo == null && targetType.BaseType != null)
    {
        return GetPropertyReference(targetType.BaseType, memberName);
    }
    return propInfo;
}

public static string GetPropertyName<T>(Expression<Func<T>> property)
{
    LambdaExpression lambdaExpression = (LambdaExpression)property;
    var memberExpression = lambdaExpression.Body as MemberExpression ?? 
        ((UnaryExpression)lambdaExpression.Body).Operand as MemberExpression;         
    return memberExpression.Member.Name;       
}

Этот код содержит несколько вспомогательных методов, поэтому ваш собственный код остается чистым.

2. Отредактируйте тестовый код следующим образом

var response = new BulkImportResponse();
var numberOfDocumentsImportedName = MockingHelper.GetPropertyName(() => response.NumberOfDocumentsImported);
MockingHelper.SetPropertyValue(response, numberOfDocumentsImportedName, 2);

BulkExecutorMock = new Mock<IBulkExecutor>();
BulkExecutorMock
    .Setup(x => x.BulkImportAsync(It.IsAny<List<TimeSeries>>(), true, true, null, null, CancellationToken.None))
    .ReturnsAsync(response);

В строке 3 вы можете ввести любое число, которое нужно вернуть в объекте BulkImportReponse. В последней строке вы затем (как обычно) подключаете объект к фиктивному вызову.

Вот мой собственный код, который мне нужен для модульного тестирования. Когда я отлаживаю, значение bulkImportResponse.NumberOfDocumentsImported равно 2 (так как оно было установлено в настройке модульного теста)

BulkImportResponse bulkImportResponse;
do
{
    bulkImportResponse = await _bulkExecutor.BulkImportAsync(batch, true);
} while (bulkImportResponse.NumberOfDocumentsImported < batch.Count);

В моих примерах кода я использовал BulkImportAsync () вместо BulkImport (), но результат должен быть таким же.

Надеюсь это поможет!

person Reggi    schedule 09.10.2019