Как сделать так, чтобы объект Moq возвращал различные значения при нескольких вызовах в тестах?

Как я могу улучшить свой класс, который предоставляет тестовые данные для метода тестирования в WPF-приложении, разработанном с использованием подхода TDD? В частности, я хотел бы узнать, как правильно сформировать и структурировать метод, который возвращает ожидаемые входные данные и результаты. Вот текущая реализация моего тестового класса: <br/> <pre><code class="cs">public class RatingRecordRepositoryTestData
{
    private static void AddRecordToList(List&lt;RatingRecord&gt; list, int index)
    {
        var record = new RatingRecord
        {
            GameCompletionDate = DateOnly.FromDateTime(DateTime.Now)
        };
        list.Add(record);
    }

    public static IEnumerable&lt;object[]&gt; GetAllMethod_TestData()
    {
        var ratingRecordsOnEasyLevel = new List&lt;RatingRecord&gt;();
        var ratingRecordsOnNormalLevel = new List&lt;RatingRecord&gt;();
        var ratingRecordsOnHardLevel = new List&lt;RatingRecord&gt;();

        ratingRecordsOnEasyLevel.Add(new RatingRecord() { GameCompletionTime = new TimeOnly(13, 45, 23), GameCompletionDate = DateOnly.FromDateTime(DateTime.Now) });
        ratingRecordsOnNormalLevel.Add(new RatingRecord() { GameCompletionTime = new TimeOnly(1, 59, 59), GameCompletionDate = DateOnly.FromDateTime(DateTime.Now) });
        ratingRecordsOnHardLevel.Add(new RatingRecord() { GameCompletionTime = new TimeOnly(21, 31, 2), GameCompletionDate = DateOnly.FromDateTime(DateTime.Now) });

        var allData = new List&lt;object[]&gt;
        {
            new object[] { DifficultyLevels.Easy, ratingRecordsOnEasyLevel },
            new object[] { DifficultyLevels.Normal, ratingRecordsOnNormalLevel },
            new object[] { DifficultyLevels.Hard, ratingRecordsOnHardLevel }
        };

        return allData;
    }
}</code></pre> <br/> <br/> И вот класс для тестов: <br/> <pre><code class="cs">public 
  • 15 сентября 2024 г. 15:53
Ответы на вопрос 2
Чтобы сделать объект Moq, который возвращает различные значения при нескольких вызовах, вы можете использовать методы `Returns` или `Returns` с указанием последовательных значений. Например:

```csharp
var mock = new Mock<IMyInterface>();
mock.Setup(m => m.MyMethod())
    .ReturnsOnce(expectedValue1)
    .ReturnsOnce(expectedValue2)
    .ReturnsOnce(expectedValue3);
```

Если вы хотите настроить возврат значений в зависимости от последовательных вызовов, вы можете использовать следующую конструкцию:

```csharp
mock.SetupSequence(m => m.MyMethod())
    .Returns(expectedValue1)
    .Returns(expectedValue2)
    .Returns(expectedValue3);
```

Это будет возвращать `expectedValue1` при первом вызове `MyMethod()`, `expectedValue2` при втором вызове, и `expectedValue3` при третьем вызове.

Теперь, что касается вашего класса `RatingRecordRepositoryTestData`, вы можете улучшить его, используя некоторые из следующих подходов:

1. **Упрощение кода**: Вместо создания отдельных списков для каждого уровня сложности, вы можете использовать один список и добавить записи на основании уровня сложности.

2. **Фабрика объектов**: Используйте метод для создания объектов `RatingRecord`, чтобы избежать дублирования кода.

3. **Система заполнения данных**: Вы можете использовать массивы или словари для упрощения структуры данных.

Вот пример улучшенной версии вашего класса:

```csharp
public class RatingRecordRepositoryTestData
{
    private static RatingRecord CreateRatingRecord(TimeOnly completionTime)
    {
        return new RatingRecord
        {
            GameCompletionTime = completionTime,
            GameCompletionDate = DateOnly.FromDateTime(DateTime.Now)
        };
    }

    public static IEnumerable<object[]> GetAllMethod_TestData()
    {
        var data = new Dictionary<DifficultyLevels, TimeOnly>
        {
            { DifficultyLevels.Easy, new TimeOnly(13, 45, 23) },
            { DifficultyLevels.Normal, new TimeOnly(1, 59, 59) },
            { DifficultyLevels.Hard, new TimeOnly(21, 31, 2) }
        };

        var allData = data.Select(pair => 
            new object[] { pair.Key, new List<RatingRecord> { CreateRatingRecord(pair.Value) } })
            .ToList();

        return allData;
    }
}
```

### Объяснение улучшений:
1. **Удаление дублирования**: Метод `CreateRatingRecord` отвечает за создание записей, что упрощает добавление новых записей и уменьшает дублирование кода.

2. **Использование коллекции**: Используя `Dictionary`, вы можете легко управлять уровнями сложности и их значениями, что делает код более читабельным.

3. **Linq для упрощения**: Использование LINQ для трансформации данных делает код более кратким и понятным.

С этими изменениями, ваш класс становится более организованным и удобным для управления тестовыми данными.
Есть подозрение, что при каждом вызове тестового метода с новым параметром экземпляр вашего тестового класса каждый раз создается заново, вместе с новым экземпляром имитатора (Mock> _ratingRecordRepositoryMock; ) . В пользу этого говорит и то, что вы при каждом вызове заново настраиваете имитатор заново, а если бы экземпляр имитатора уже использовался, вы бы получили ошибку. 

Проверить просто: поставьте точку остановки (или воткните отладочное сообщение) в конструктор тестового класса.
Похожие вопросы