Здравствуйте, another_coder, Вы писали:
_>Здравствуйте, gandjustas.
_>Вы делаете вывод о нужности юнит-тестов, руководствуясь частным случаем и основываясь на последствиях. Конечно, практика вносит свои корректировки: непрофессионализм девелоперов, ошибки проектирования и пр., которые следует учитывать. Но то кол-во случаев на каждом IF или SWITСH, которое можно проверить unit-tests вы никогда не проверите интеграционными. Либо это превратится в такой головняк с интеграционными, когда написание тестов жрет не меньше временеи, чем сам функционал.
Ты подменяешь понятия. Если можно написать юнит тест, это вовсе не означает, что им можно что-то проверить. Я несколько раз видел программы с тестовым покрытием близким к 100%, естественно юнит-тестами. В них были баги в огромном количестве.
_>То о чем вы говорите стандартные заблуждения. Если бы были интеграционные тесты, то ошибки были бы замечены. Но какой-то человек решил их не делать. А вывод, почему-то, делается не о его профессионализме, а о ненужности unit-test, которые свою работу сделали на отлично, как мне кажется. Как долго вы искали проблему?
Если у бабушки кое-что было, то она была бы не совсем бабушкой. Не делали интеграционные тесты по причине, описанной тобой выше. Покрыть интеграционными тестами значимую часть программы с разумными затратами невозможно. А юнит тестами можно. Вот только юнит-тесты не проверяют.
G>>Вот у тебя есть метод — делает выборку, обрабатывает, сохраняет. Это твой сценарий.
G>>Для теста ты репозиторий подменяешь банальной реализацией на list<t>, когда метод просто отдает список. И проверяешь что данные в списке поменялись.
G>>Ты написал код, который делает обработку, а SaveChangesAsync забыл. Тест проходит. При запуске не работает, тупо ничего не происходит.
_>Я бы распилил на несколько методов, которые по-отдельности можно протестировать. Т.е. выборка проверялась бы интеграционным тестом, обработка — юнит тестом, сохранение — интеграционным. При этом в юнит тесте выборку и сохранение я бы замокал.
По-моему ничего бы не изменилось.
Код был примерно такой:
void F(Repo repo)
{
var xs = repo.GetXsByXXX();
foreach(var x in xs)
{
x.y+=1;
}
// repo.SaveChanges(); // эту строку потеряли при рефакторинге
}
[Test]
void Test()
{
var mock = new Mock();
var xs = new [] { ... };
mock.Xs = xs;
F(mock);
Assert.IsTrue(xs.All(...));
}
Тест зеленый, код не работает.
Ты предлагаешь покрыть GetXsByXXX интеграционным тестом, что не имеет смысла, там примитивный запрос. Покрыть SaveChanges интеграционным тестом, что тоже не имеет смылса и написать юнит-тест для F, что не дает фактически проверки.
G>>У тебя два варианта:
G>>1) Делать реальный транзакционный inmemory storage. Но я таких не видел за 10 лет (слава богу в EFCore его сделали).
_>Сделать можно, но не надо.
Когда есть готовый, то не надо. А раньше пытался такое избразить, но как-то дофига сложно получилось.
G>>2) Проверять integration-тестом, но с ними проблем еще больше — медленно, тестовые данные нужны, писать тесты сложнее.
_>Корень проблемы не в интеграционных тестах, а в людях, которые не умеют их писать. Как обычно, впрочем. Скорее всего, описанные тесты у вас больше похожи на end-to-end тесты, когда вы запускаете целиков всё (ВСЁ). Такие действильно очень тяжелые во всех отношениях. Тут необходимо помнить, что задача тестов (любых): проверить работу известного сценария. Приэтом, юнит — алгоритмы проверяет, а интеграционный только связку. Если перемешивать, то возникают проблемы.
Это словоблудие. Известный сценарий я привел, как его юнит-тестом проверить?
У меня еще интереснее сценарий есть:
В приложении календарь. При создании нового события надо проверить, что событие не пересекается с существующим. Событий много, тянуть все в память нельзя, проверять надо запросом к базе. И важное условие — тебе повезло, ты не можешь использовать Linq, обязательно текстовые запросы.
Напиши
юнит-тест для проверки.
Я этой задачей троллю апологетов юнит-тестов.