Безопасен ли поток внедрения контекста SpecFlow?

#c# #bdd #specflow #context-injection

#c# #bdd #specflow #внедрение контекста

Вопрос:

Я использую SpecFlow для запуска пакета тестирования интеграции API, который предоставит актуальную документацию и тестовое покрытие нового пользовательского интерфейса API. У меня записано несколько функциональных файлов, и я, наконец, дошел до того, что пытаюсь запустить около 60 тестов параллельно. Однако, хотя я могу запускать функции по отдельности без проблем, я сталкиваюсь с периодическими сбоями при попытке запустить их все параллельно с помощью тестового модуля Visual Studio 2019 и плагина xUnit runner.

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

 Scenario: Retrieving Message records returns data
Given I have created the following ClientAccounts:
    | Index | SiteID | IsActive |
    | 1     | 1      | 1        |
And I have created the following Logins:
    | Index | IsActive |
    | 1     | 1        |
And I have created the following Messages:
    | MessageID | MessageText |
    | 1         | Asdfasdf    |
When I send an authentication request using the first Login and the IP Address 127.0.0.1
And I send a read request to the v1 Message endpoint for the first Message record created:
Then the first Message response should be equivalent to the following data for the first Message record created:
    | MessageTest |
    | Asdfasdf    |
  

Первые три шага принадлежат классу с именем DatabaseSteps, конструктор которого принимает экземпляр класса DataUtility, который облегчает операции CRUD с базой данных и отслеживает, какие записи были созданы в рамках выполнения теста. Также существуют некоторые привязки [StepArgumentTransformation], которые преобразуют эти таблицы в объекты базы данных, которые могут быть вставлены в БД.

Четвертый, пятый и шестой шаги относятся к дополнительным классам шагов, конструкторы которых используют в качестве зависимостей как DataUtility для доступа к БД, так и ApiClientContext, в котором хранится информация о сеансе, а также информация об ответах API, которые были ранее сохранены, чтобы подтвердить фактические ответы, полученные на этапе «Затем». Класс DataUtility реализует IDisposable для упрощения очистки после тестирования.

Основываясь на документации, я ожидал, что классы контекста, введенные через встроенный контейнер DI, будут потокобезопасными. Однако я обнаружил, что как при использовании DataUtility, реализующей IDisposable, так и при отсутствии объявления интерфейса и простом вызове Dispose() непосредственно из [AfterScenario] перехвата, тесты, связанные с ожидаемыми возвращаемыми данными, завершаются неудачей во время большинства тестовых запусков. Трудно сказать наверняка, потому что устранение неполадок с параллелизмом ужасно, но кажется, что экземпляр DataUtility совместно используется сценариями, и когда какой-либо данный сценарий вызывает Dispose() в утилите обработки данных, все тестовые данные, которые я собрал, удаляются — даже в других несвязанных сценариях. Когда у меня нет ни IDisposable, объявленного в классе DataUtility, ни вызова Dispose() из перехвата, тесты выполняются без инцидентов. Есть ли определенный способ, которым мне нужно настроить введенный класс контекста, чтобы каждый сценарий получал свой собственный экземпляр этого класса?

Другие подробности: VS2019, SpecFlow 3, тестовый запуск xUnit

Комментарии:

1. Является ли DataUtility классом, который вы создали?

2. Да — DataUtility — это класс, который я написал для облегчения взаимодействия с базой данных. Он принимает в качестве параметра конструктора экземпляр IConfiguration (который зарегистрирован в ConfigurationBuilder в перехватчике BeforeScenario) и используется для получения строк подключения к базе данных. Он содержит в качестве членов экземпляры двух классов ProductDatabase и STSDatabase , которые сами содержат оба класса поставщиков баз данных (которые выполняют операции CRUD в своих соответствующих базах данных) и различный список<T>, где T — объект базы данных POCO, которые были созданы, чтобы их можно было удалить по завершении сценария.

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

4. Я думаю, что урезанной версии было бы достаточно.

5. Вчера я потратил кучу времени на изучение этого, а сегодня добавил ведение журнала в свой DAL, чтобы выяснить, что делается с созданием и удалением объектов в базе данных, и в конечном итоге обнаружил очень скрытую ошибку в библиотеке assist, которую я использую с Dapper; он собирал поле, имя которого заканчивалось на GUID, и использовал его в автоматически созданном запросе на удаление вместо использования фактического поля ID. Поскольку библиотека объектов, которую я использую для базы данных, является общей для команды разработчиков, я не могу украсить ее своими собственными атрибутами и впал в безумие из-за этой проблемы. Спасибо @GregBurghardt за помощь!

Ответ №1:

Вчера я потратил кучу времени на изучение этого, а сегодня добавил ведение журнала в свой DAL, чтобы выяснить, что делается с созданием и удалением объектов в базе данных, и в конечном итоге обнаружил очень скрытую ошибку в библиотеке assist, которую я использую с Dapper; он собирал поле, имя которого заканчивалось на GUID, и использовал его в автоматически созданном запросе на удаление вместо использования фактического поля ID. Поскольку библиотека объектов, которую я использую для базы данных, является общей для команды разработчиков, я не могу украсить ее своими собственными атрибутами и впал в безумие из-за этой проблемы. Спасибо @GregBurghardt за помощь!