Модульный тест.Почему Mock возвращает другое значение во второй итерации цикла? - PullRequest
3 голосов
/ 06 марта 2019

У меня есть макет на следующий объект, работа которого заключается в сборе данных датчика.Он реализует этот интерфейс:

public interface ISensorDataCollector
{
    List<int> CollectSensorData(int amountOfValues);        
}

Внутри теста у меня есть следующая схема:

// ARRANGE
var collector = new Mock<ISensorDataCollector>() { CallBase = true };
// Mock SensorDataCollector
collector.Setup((x) => x.CollectSensorData(10)
         .Returns(new List<int> { 1,2,3,4,5,6,7,8,9});
myProcess.AdwSensorDataCollector = collector.Object;

// ACT
myProcess.CollectSensorDataRepeatIfFails(5);

У тестируемого метода есть цикл while, в котором собираются данные.Простая версия выглядит следующим образом:

public ISensorDataCollector SensorDataCollector { get; set; }

public void CollectSensorDataRepeatIfFails(int counterForRepeatedMeasurement)
{        
    do
    {
        List<int> values = this.SensorDataCollector.CollectSensorData(10); 

        values.Clear();

        counterForRepeatedMeasurement--;
    } while (counterForRepeatedMeasurement >= 0);
}

Проблема: со второй итерации в строке this.AdwSensorDataCollector.CollectSensorData(10); возвращает пустой список.Но я ожидал, что он будет возвращать значение, которое я указывал в настройке каждый раз:

collector.Setup((x) => x.CollectSensorData(10)
         .Returns(new List<int> { 1,2,3,4,5,6,7,8,9});

Я думаю, это как-то связано с values.Clear();, потому что, если я уберу очистку списка.Возвращаемое значение CollectSensorDataRepeatIfFails остается одинаковым на всех итерациях, и моя проблема исчезает.Но это только предположение.Я действительно хотел бы знать, почему второй вызов не возвращает указанное возвращаемое значение.

Вопрос: что я пропустил?Работает ли установка только для 1 вызова mocked-метода?или есть кнопка, которую я забыл нажать, чтобы заставить ее вести себя ожидаемым образом?Почему очистка списка влияет на возвращаемое значение второго вызова проверяемого метода?Кто-нибудь может пролить свет на этот вопрос?

Я провел небольшое исследование, прежде чем спрашивать, но я мог только найти посты, которые объясняют, как заставить макет возвращать различные значения при следующем вызове.Но нет поста, где проблема возникает с использованием цикла.Я также был бы рад дубликатам чаевых.

Cheers

Ответы [ 2 ]

4 голосов
/ 06 марта 2019

Это ожидаемое поведение, потому что вы работаете со ссылками из этого списка. Неважно, возвращаете ли вы его из макета или откуда-то еще.

collector.Setup((x) => x.CollectSensorData(10)
    .Returns(new List<int> { 1,2,3,4,5,6,7,8,9});

Вы регистрируетесь ссылка на List<int>.

List<int> values = this.AdwSensorDataCollector.CollectSensorData(10);

Таким образом, в этом вызове вы получаете ссылку на точно такой же List<int> объект.

values.Clear()

При вызове этого удаляются все элементы для того же точного списка , который должен вернуть макет.

Подробнее о ссылочных типах вы можете прочитать здесь: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types

Обновление: Вы можете использовать .Callback(() => new List<int>() { 1, 2, 3}) вместо Returns. В этом случае обратный вызов будет вызываться при каждом вызове фиктивной функции.

4 голосов
/ 06 марта 2019

Что я пропустил?Работает ли установка только для 1 вызова смоделированного метода?

На самом деле это ожидаемое поведение макета.Настройка изменчива в том смысле, что вы можете изменить захваченные аргументы или вернуть значение по ссылке.Вы делаете именно это к values.Clear();.Чтобы избежать этой проблемы, просто отложите создание списка, предоставив фабрику.Как то так:

collector.Setup((x) => x.CollectSensorData(10))
    .Returns<int>((i) => new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
...