Страницы

Поиск по вопросам

среда, 3 апреля 2019 г.

Тестирование слоя валидации данных

Люди, подскажите хорошее решение. Имеется некий слой валидации данных, бизнес логики приложения и слой работы с БД. Есть правила накладывающие ограничения (например у некоторых сущностей есть 2 идентификатора [Guid и string определенной длины] и должно контролироваться отсутствие их дублирования с помощью валидации). При всем этом, при написании тестов возникает кейс что мы тестируем работу слоя валидации и нужно что бы валидация не прошла (т.к. в базе такой объект уже существует), либо наоборот прошла так как такого объекта еще нет. И тут возникает проблема потому что мы не знаем что есть а чего нет в тестовой базе. Возможный вариант решения это заранее наполнить базу некими сущностями и постоянно работать с ними, но такой подход не нравится тем что при написании теста приходится помнить что у нас есть в базе (да и тем более в других тестах могу создаваться новые сущности), а хочется что бы мы могли в каждом тесте создать N удобных для этого теста объектов и работать с ними. Но такой подход с обычной базой невозможен так как единственное решение которое я вижу, это нужно будет чистить базу в каждом тесте а это не быстро во всяком случае. Что приходит в голову это создать мок объекты работы с базой которые можно будет чистить в каждом тесте или создавать заново но это не кажется изящным решением. Собственно как решить проблему изящно ? PS. Язык C# тесты встроенные в visual studio, использую entity-framework


Ответ

Ваши юнит-тесты вообще не должны подключаться к базе данных. Вы должны тестировать только ваш класс, отвечающий за валидацию, все внешние зависимости этого класса надо заменить моками. У вас должен быть мок, который будет притворяться, что он подключается к базе данных, но на деле никуда не подключаться, а просто возвращать подходящие для тестирования значения.
На примере одного теста. Скажем вы проверяете, что в базе нет двух элементов с одинаковым id. Тогда тест должен выглядеть примерно так:
создаете мок класса для доступа к базе данных настраиваете нужный метод этого мока так, чтобы он возвращал заведомо неверные данные передаете этот мок валидатору и убеждаетесь, что валидация не прошла
Код примерно такой (в примере используется nunit и moq):
[Test] public void Test1() { // arrange var dbRepositoryMock = new Mock(); dbRepositoryMock.Setup(x => x.GetItems()).Returns(new [] {new Item(){ Id = 1}, new Item(){ Id = 1}}); var validator = new Validator();
// action var validationResult = validator.ValidateItemsUnique(dbRepositoryMock.Object);
// assert Assert.IsFalse(validationResult); }
То есть идея вот в чем: вы создаете реальный экземпляр ТОЛЬКО для тестируемого класса. В нашем случае тестируемый класс - валидатор. Для всех зависимостей вы создаете моки, и настраиваете у этих моков ТОЛЬКО те методы, которые используются тестируемым функционалом, все остальные просто игнорируете. Например, у вашего репозитория может быть еще 20 методов, возвращающих разные сущности из базы данных, но если вы проверяете уникальность Item'ов, вы настраиваете возвращаемое значение только метода GetItems(), а все остальные методы просто игнорируете.
Про подключение к базе данных вообще забудьте, ваши юнит тесты должны выполняться без этого. Если вам сложно написать код в таком стиле - без реального подключения к базе данных, значит он плохо приспособлен для юнит-тестирования, и надо выполнять его рефакторинг.
Ваша попытка написать юнит-тесты с подключением к реальной бд довольно типичная ошибка для начинающих писать юнит-тесты. Я тоже пытался делать так и видел как то же самое пытаются сделать другие. Эта ошибка указывает на то, что вы не до конца понимаете смысл юнит-тестирования и вам стоит потратить время на чтение какой нибудь книги по этой теме. "Разработка через тестирование" Кента Бека будет отличным вариантом.

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

Отправить комментарий