Считается ли модульный тест хрупким, если он проваливается при изменении бизнес-логики?

27

Пожалуйста, смотрите код ниже; Он проверяет, имеет ли право лицо с Полом женщины на предложение1:

[Fact]
public void ReturnsFalseWhenGivenAPersonWithAGenderOfFemale()
{
    var personId = Guid.NewGuid();
    var gender = "F";
    var person = new Person(personId, gender);

    var id = Guid.NewGuid();
    var offer1 = new Offer1(id,"Offer1");
    Assert.False(offer1.IsEligible(person));
}

Этот модульный тест пройден успешно. Тем не менее, он потерпит неудачу, если «Предложение1» будет предложено женщинам в будущем.

Допустимо ли говорить - если бизнес-логика, окружающая предложение 1, изменится, тогда должен измениться модульный тест. Обратите внимание, что в некоторых случаях (для некоторых предложений) бизнес-логика изменяется в базе данных следующим образом:

update Offers set Gender='M' where offer=1;

и в некоторых случаях в модели предметной области, как это:

if (Gender=Gender.Male)
{
  //do something
}

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

w0051977
источник
2
Подумайте с другой стороны: хотите ли вы иметь тесты, которые не были бы неудачными, когда вы меняете логику в тестируемой системе?
Фабио

Ответы:

77

Это не хрупкий в обычном смысле. Модульный тест считается хрупким, если он ломается из-за изменений реализации, которые не влияют на тестируемое поведение. Но если сама бизнес-логика изменится, то тест этой логики должен прерваться.

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

Название теста Returns False When Given A Person With A Gender Of Femaleне описывает бизнес-правило. Бизнес-правило было бы что-то вроде Offers Applicable to M should not be applied to persons of gender F.

Таким образом, вы можете написать тест, который подтверждает, что если предложение определено как применимое только к лицам типа M, то лицо типа F не будет указано в качестве отвечающего критериям. Этот тест обеспечит работу логики даже в случае изменения конфигурации конкретных предложений.

JacquesB
источник
@JaquesB, тогда это не будет модульный тест? или это будет? Я считаю, что если бы база данных была задействована, это был бы интеграционный тест. Это правильно? Вы говорите, не используйте модульные тесты, если бизнес-логика сильно меняется?
w0051977
@ w0051977: Зависит от того, как вы пишете тест. Если тест включает в себя фактическое изменение, изменение чего-либо в базе данных, то это будет интеграционный тест.
JacquesB
3
@ w0051977 лучшая идея - не заставляйте репозиторий зависеть от компонента, отвечающего за реализацию бизнес-правил. Создайте оркестровку более высокого уровня, которая вызывает хранилище, а затем вызывает бизнес-правила. Теперь вы можете самостоятельно тестировать бизнес-правила.
Муравей P
5
@ w0051977 конечно, это так - тесты определяют поведение. Если правила, регулирующие поведение компонента, изменяются, то тесты должны измениться, чтобы отразить изменение в поведении. Что не нужно менять, так это тесты, которые определяют поведение, отличное от того, что меняется. Если поведение определяется базой данных, то тест, охватывающий некоторый другой код, по своей сути не связан и не должен изменяться, если только логика базы данных не входит в область действия теста. Эта область для вас, чтобы определить, и семантика того, является ли это модульным тестом или интеграционным тестом, не очень важна.
Муравей P
3
@ w0051977 несколько расширяет эту идею, если бизнес-логика меняется или исправлена ​​ошибка и тесты не нуждаются в корректировке, это признак того, что тесты не охватывают достаточных случаев и, как правило, должны быть расширены.
Морген
14

Когда свойство определено в производственной базе данных (или клоне для тестирования), это не модульный тест . Юнит-тест проверяет единицу работы и не требует определенного внешнего состояния для работы. Это предполагает, что Offer1в базе данных определено предложение только для мужчин. Это внешнее состояние. Так что это скорее интеграционный тест , а именно тест системы или приемочный тест. Обратите внимание, что приемочные тесты часто не пишутся в сценарии (не запускаются в тестовой среде, а выполняются вручную людьми).

Когда свойство определяется в модели предметной области с помощью ifоператора, этот же тест является модульным тестом. И это может быть хрупким. Но настоящая проблема в том, что код хрупок. Как правило, ваш код будет более устойчивым, если бизнес-поведение настраивается, а не жестко запрограммировано. Потому что быстрое развертывание для исправления небольшой ошибки кодирования должно быть редким. Но деловое требование, изменяющееся без уведомления, - это просто вторник (то, что происходит еженедельно).

Возможно, вы используете модуль модульного теста для запуска теста. Но рамки модульных тестов не ограничиваются выполнением модульных тестов. Они могут и действительно выполнять интеграционные тесты.

Если бы вы писали модульный тест, вы бы создали и то personи другое offer1с нуля, не полагаясь на состояние базы данных. Что-то типа

[Fact]
public void ReturnsFalseWhenGivenAPersonWithAGenderOfFemale()
{
    var personId = Guid.NewGuid();
    var gender = "F";
    var person = new Person(personId, gender);

    var id = Guid.NewGuid();
    var offer1 = new Offer1(id, "ReturnsFalseWhenGivenAPersonWithAGenderOfFemale");
    offer1.markLimitedToGender("M");

    Assert.False(offer1.IsEligible(person));
}

Обратите внимание, что это не меняется в зависимости от бизнес-логики. Это не утверждение, что offer1отвергает женщин. Это делает offer1предложение, которое отвергает женщин.

Вы можете создать и настроить базу данных как часть теста. В C #, используя NUnit или в Java JUnit, вы бы настраивали базу данных в Setupметоде. Предположительно, ваша тестовая среда имеет аналогичное понятие. В этом методе вы можете вставлять записи в базу данных с помощью SQL.

Если вам трудно писать код, который заменяет тестовую базу данных для рабочей базы данных, это звучит как слабость тестирования в вашем приложении. Для тестирования было бы лучше использовать что-то вроде внедрения зависимостей, которое допускает подстановку. Затем вы можете написать тесты, которые не зависят от текущих бизнес-правил.

Дополнительным преимуществом этого является то, что владельцу бизнеса часто проще (не обязательно корпоративному владельцу, а скорее человеку, ответственному за этот продукт в корпоративной иерархии) настраивать бизнес-правила напрямую. Потому что, если у вас есть такая техническая структура, владельцу бизнеса легко разрешить использовать пользовательский интерфейс (UI) для настройки предложения. Владелец бизнеса выберет ограничение в пользовательском интерфейсе и выдаст markLimitedToGender("M")вызов. Затем, когда предложение сохраняется в базе данных, оно будет хранить это. Но вам не нужно хранить предложение использовать его. Таким образом, ваши тесты могут создать и настроить предложение, которого нет в базе данных.

В вашей системе, как описано, владелец бизнеса должен будет отправить запрос технической группе, которая выдаст соответствующий SQL и обновит тесты. Или техническая группа должна отредактировать ваш код и тесты (или тесты, а затем код). Это кажется довольно тяжелым подходом. Ты можешь это сделать. Но ваше программное обеспечение (а не только тестирование) было бы менее хрупким, если бы вам не пришлось это делать.

TL; DR : вы можете писать такие тесты, но вам лучше писать свои программы, так что вам не нужно это делать.

mdfst13
источник
Я даю свободу в улучшении некоторых мелких деталей в вашем ответе. Пожалуйста, проверьте, правильно ли я поняла ваши намерения.
Док Браун
В исходном посте нет указания на то, что база данных задействована. Утверждение, что это предполагает, что Offer1 уже находится в базе данных, является странным.
Уинстон Эверт
2
@WinstonEwert: есть четкое указание, вы должны прочитать вопрос более внимательно. Я не осознавал этого и при первом прочтении, но это действительно то, о чем говорит ОП.
Док Браун
@ mdfst13, прости, что пропустил. Однако OP говорит, что условия иногда находятся в базе данных, а иногда в модели предметной области. Ваш ответ отлично подходит для первого случая и непочтительно во втором. Если вы отредактируете свой ответ, чтобы прояснить этот вопрос, я уберу свое поспешное понижение.
Уинстон Эверт