Вопрос

Тестирование работы запросов через AppConnection

Добрый день. 

Для интеграции bpm'online с внешней системой планируется разработать библиотеку, подключаемую как dll, и вызываемую через БП. В библиотеке планируется использование стандартных методов работы с данными через ESQ, которые требуют для своей работы UserConnection и AppConnection. Хотелось бы покрыть библиотеку тестами, так что бы можно было отлаживать работу библиотеки и покрыть ее модульными тестами. 

Как и многие столкнулся с проблемой, что не понятно как и можно ли вообще вне bpmonline создать полноценно работающий AppConnection. Есть ли такая возможность? 

Нравится

10 комментариев

попробовал сам создать и инициализировать AppConnection и UserConnection, ожидаемо пришел к выводу, что они завязаны на множество недокументированных классов, частично статических, частично завязанных через DI, в том числе логирование и т.п., думаю в текущей реализации bpm'online создать UserConnection вне веб приложения практически нереально. Очень жаль, такая реализация не дает полноценно производить разработку со стандартными механизмами доступа к данным, так как отладить или сделать тесты вне веб приложения невозможно.

Nevg,

Есть статья, где описаны рекомендации по созданию unit тестов: https://academy.terrasoft.ru/documents/technic-sdk/7-12/kak-sozdat-unit…

Если необходимо создавать AppConnection и UserConnection для написания unit тестов, то в ядре есть классы TestAppConnection и TestCoreUserConnection, они находятся в Terrasoft.TestFramework.

 

Спасибо, очень похоже на решение моей задачи! Жаль только,  что информации про эти классы чуть меньше чем ноль )))), на той же академии про них не написано ничего (https://academy.terrasoft.ru/search/TestCoreUserConnection). Есть немного на https://academy.terrasoft.ru/api/netcoreapi/7.12.0/index.html#GeneralSD…. У Вас нет случайно еще примера как их правильно использовать? ))

Tsopa,

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

Коллеги, также столкнулся с необходимостью выполнения запросов к БД в NUnit-тестах. Так и не понял, как можно в данном случае прикрутить TestAppConnection, говорит, что not valid in current context. Удалось ли решить проблему? Не поделитесь ли примерами удачного использования TestAppConnection?

Андронов Олег Алексеевич,

Пример мока запросов в БД:

var appConnection = Substitute.For<AppConnection>();
var userConnection = new TestUserConnection(appConnection);
var dbExecutor = Substitute.For<DBExecutor>(userConnection);
// Настраиваем UserConnection для корректной работы без БД
userConnection.DBExecutor = dbExecutor;
userConnection.DBEngine = Substitute.For<DBEngine>();
userConnection.DBTypeConverter = new TestDBTypeConverter();
// Подготавливаем структуру таблицы результата
var testData = new TestData()
    .Structure()
        .AddStringColumn("Code")              // Формируем список колонок результата
        .AddBoolColumn("CanExecute")
    .NewRow("CanManageAdministration", false) // Формируем записи результата
    .NewRow("CanManageData", true);
DataTable dataTable = testData.GetDataTableUseDataStructure();
// Пример №1: Мокаем запрос к БД, который содержит слово "SysAdminOperation" и значение одного из параметров = "CanManageAdministration"
dbExecutor.ExecuteReader(ArgExt.Contains("SysAdminOperation"), ArgExt.ContainsQueryParameterByValue("CanManageAdministration"))
    .Returns(dataTable.CreateDataReader());
// Пример №2: Мокаем запрос к БД, который содержит слово "SysAdminOperation" и любой параметр
dbExecutor.ExecuteReader(ArgExt.Contains("SysAdminOperation"), Arg.Any<QueryParameterCollection>())
    .Returns(dataTable.CreateDataReader());
// Пример №3: Мокаем все запросы к БД с любыми параметрами
dbExecutor.ExecuteReader(Arg.Any<string>(), Arg.Any<QueryParameterCollection>())
    .Returns(dataTable.CreateDataReader());

Структура так же доступна из свойства testData.DataStructure.

Часто таблица результатов с тестовыми данными становится слишком объемной чтобы делать ее вручную. Чтобы не заботиться о ручном формировании структуры выборки рекомендуется использовать метод GetTestDataWithStructure в классе UnitTestUtilities:

Пример использования GetTestDataWithStructure:

/* Следующая строка подготовит экземпляр класса TestData с готовой к наполнению структурой,
 * содержащей колонки из всех схем, что указаны в массиве string [] schemaNames.
 * Условием корректной работы является наличие схем с такими именами в entitySchemaManager.
 */
TestData testData = UnitTestUtilities.GetTestDataWithStructure(entitySchemaManager, "Activity");

Вместо последовательного добавления значений в строку выборки в TestDataStructure существует метод AddRowWithCustomColumnValues для добавления строки со значениями в конкретных колонках в виде Dictionary<string, object>, где ключ - имя колонки, а значение - соответствующее значение в данной колонке, неуказанные значения автоматически будут заполнены как DBNull.Value:

Пример использования AddRowWithCustomColumnValues:

...
var customValues = new Dictionary&lt;string, object&gt; {
    {"Sender", "johndoe@example.com"}
};
testData.DataStructure.AddRowWithCustomColumnValues(customValues);

 

Tsopa,

Спасибо за такой подробный ответ!

Я правильно понимаю, что в тестировании не получится делать запросы в реальную базу? То есть мы можем удобно и быстро создавать моки и наполнять их данными, после чего делать к ним запросы из тестов так же, как делает боевой метод, но это все равно будет запрос к тестовой структуре, а не к реальной базе? 

Пример: есть условный сервис, который создает обращение. Я могу сделать в [Test] методе запрос к этому сервису обычным WebHttpRequest, проверить, что он отвечает, и отвечает так, как должен. Но в данном случае я не могу быть уверен, что обращение действительно создалось в базе. Возможно, я хотел бы проверить, что в нем произошли некоторые изменения, которые предусмотрены логикой создания записи и т.д. Как можно решить эту задачу?

 

Андронов Олег Алексеевич,

Проверка SqlText, вычитки данных или конвертацию типов, которые зависят от конкретной СУБД

Тест на проверку SqlText, вычитанных из базы данных или конвертацию типов, которые зависят от конкретной СУБД

Описание:

Тест следует располагать в проектах Terrasoft.DB.Oracle.Tests и Terrasoft.DB.MSSql.Tests, если реализация теста отличается для конкретной СУБД. Тест необходимо реализовать для каждой СУБД.

Чаще всего, таким образом, реализуются тесты на проверку SqlText, вычитанных из базы данных или конвертацию типов которые зависят от конкретной СУБД.

Наследуется от:

MSSqlBaseDBTestCase или OracleBaseDBTestCase

Доступно:

Все методы и свойства, описанные в п. 1) и 2) наследованные от классов BaseCommonTestCase, BaseCoreTestCase и BaseDBTestCase, но перегружают методы возвращающие типы определенные для конкретной СУБД.

DBEngine CreateDBEngine()

DBSecurityEngine CreateDBSecurityEngine()

DBMetaScript CreateDBMetaScript(UserConnection userConnection)

DBMetaEngine CreateDBMetaEngine(UserConnection userConnection)

DBExecutor CreateDBExecutor(UserConnection userConnection)

Пример:

Константа типа DateTime собирается для Oracle и для MSSql по разному. Поэтому реализуем тест для двух СУБД

// Тест для MSSql
// Тестируется как собирается Sql для константы типа DateTime
[Test, Category("PreCommit")]
public void GetSqlText_ReturnCorrectSqlText_UseDataTimeParameter() {
        // Создаем экземпляр MSSqlEngine
        var dbEngine = CreateDBEngine();
        // Создаем пустой и легкий UserConnection без ненужных нам инициализаций
        var currentConnection = CreateEmptyUserConnection();
        // Самостоятельно задаем DBEngine для экземпляра UserConnection
        currentConnection.DBEngine = dbEngine;
        var dateTimeValue = new DateTime(1900, 2, 3, 4, 5, 6, 7);
        var select =
                new Select(currentConnection)
                        .Column(Column.Const(dateTimeValue))
                .From("City");
        string sqlText = select.GetSqlText();
        string testSqlText =
                "\nSELECT" +
                        "\n\tCONVERT(DATETIME, '1900-02-03 04:05:06.007', 121)" +
                "\nFROM" +
                        "\n\t[dbo].[City]";
        Assert.AreEqual(testSqlText, sqlText);
}
 
// Тест для Oracle
// Тестируется как собирается Sql для константы типа DateTime
[Test, Category("PreCommit")]
public void GetSqlText_ReturnCorrectSqlText_UseDataTimeConstant() {
        // Создаем экземпляр OracleEngine
        var dbEngine = CreateDBEngine();
        // Создаем пустой и легкий UserConnection без ненужных нам инициализаций
        var currentConnection = CreateEmptyUserConnection();
        // Самостоятельно задаем DBEngine для экземпляра UserConnection
        currentConnection.DBEngine = dbEngine;
        DateTime dateTimeValue = new DateTime(1900, 2, 3, 4, 5, 6, 7);
        Select select =
                new Select(currentConnection)
                        .Column(Column.Const(dateTimeValue))
                .From("City");
        string sqlText = select.GetSqlText();
        string testSqlText =
                "\nSELECT" +
                        "\n\tTO_TIMESTAMP('1900-02-03 04:05:06.007', 'YYYY-MM-DD HH24:MI:SS.FF3')" +
                "\nFROM" +
                        "\n\t\"TEST\".\"City\"";
        Assert.AreEqual(testSqlText, sqlText);
}

 

Где взять методы: CreateDBEngine(), CreateEmptyUserConnection()?

Tsopa,

System.TypeInitializationException: 'The type initializer for 'NSubstitute.Core.SubstitutionContext' threw an exception.'

Показать все комментарии