2

У меня есть метод, который имеет много условий в нем:Есть ли более простой способ обработки модульного тестирования методом со слишком многими условиями?

public bool IsLegalSomething(Order order) 
{ 
    var item0 = order.Items.SingleOrDefault(x => x.ItemCode == "ItemCode0"); 
    var item1 = order.Items.SingleOrDefault(x => x.ItemCode == "ItemCode1"); 
    ... 
    var itemN = order.Items.SingleOrDefault(x => x.ItemCode == "ItemCodeN"); 

    return ((item0.Status == Status.Open) && (item1.Status == Status.Closed) 
     && ... 
     && (itemN.Status == Status.Canceled)); 
} 

Я хочу модульное тестирование этой функции, но есть очень много условий, что число единичных испытаний является сумасшедшим, если вы считаете, каждая комбинация. В этом операторе return есть 16 условий, и поскольку каждое условие истинно/ложно, это 2^16 различных комбинаций, которые мне нужно будет проверить. Мне действительно нужно создать 2^16 различных модульных тестов здесь, чтобы гарантировать, что все условия используются? Имейте в виду, это простой пример. Некоторые из моих функций имеют сложные условия в связи с юридическими требованиями:

return (condition0 && condition1 && (condition2 || condition3) 
    && (condition4 || (condition5 && condition6)) ...) 

По математике некоторых из моих функций, число различных комбинаций, что условия могут производить миллионы! Я изучил тесты с данными (DDUT), а также Parameterized Unit Tests (PUT), но это просто делает так, чтобы модульный тест был «заполнением пробелов». Я все еще должен поставлять все различные комбинации и ожидаемый результат! Например:

// Parameterized Unit Test 
[TestCase(..., Result = true)] // Combination 0 
[TestCase(..., Result = true)] // Combination 1 
[TestCase(..., Result = false)] // Combination 2 
public bool GivenInput_IsLegalSomething_ReturnsValidResult(...) { } 

Если я использую MSTest тянуть в качестве источника данных (CSV, например), я до сих пор с той же проблемой. У меня слишком много комбинаций, которые дают разные результаты. Есть ли альтернатива, о которой я просто не знаю?

+2

Возможно, дизайн необходимо будет пересмотреть и реорганизовать. Сложность/трудности при создании модульных тестов - это показатель того, насколько чистым является тестируемый код. Проверяемый метод делает слишком много. – Nkosi

+0

@Nkosi, Понятно, но я не совсем уверен, как «упростить» законодательные требования, которые имеют много комбинаций. В конце дня в каком-то классе будет содержаться логика, в которой говорится: «16 условий приводят логическое значение». – michael

+0

@Nkosi - это правильно. –

ответ

1

В то время как я согласен с комментариями, сделанными о рефакторинге вашего кода, я думаю, что более краткий ответ оправдан, чтобы объяснить, что именно имеется в виду «Возможно, проект необходимо будет пересмотреть и реорганизовать».

Посмотрим на следующее утверждение: A && B && (C || D);. Обычно вы говорите, что у вас есть 4 входа @ 2 варианта/каждый, или 16 комбинаций. Однако, если вы реорганизовываете вещи, вы можете уменьшить сложность. Это будет зависеть от вашего бизнес-домена, поэтому я буду использовать домен торгового сайта в качестве примера (мы фактически не знаем ваш бизнес-домен).

  • A: New Order
  • Б: Все товары на складе
  • C: Является ли Клиент Elite член
  • D: Есть Код скидки FREESHIP Используется

Причина, почему я выбрал этот сценарий должен продемонстрировать, что возможно, что C/D фактически ссылается на то, должна ли бесплатная доставка включать или нет.

  • E: отгружает Free

Теперь вместо A && B && (C || D) мы имеем A && B && E, что 3 условия @ 2 варианта/каждый, или 8 комбинаций. Разумеется, необходимо также проверить состав E, но C || D имеет только 2 варианта: 2 варианта/каждый, или 4 комбинации. Мы сократили количество полных комбинаций с 16 до 12. Хотя это может показаться не очень большим, масштаб этого сценария был намного меньше. Если вы можете объединить логические условия вместе и уменьшить ситуацию дальше, вы можете перейти от миллионов комбинаций к нескольким сотням, что намного проще в обслуживании.

Кроме того, в качестве бонуса иногда ваша логика домена изменяется в некоторых аспектах, но не в других. Представьте, что вы решили, что коммерческие клиенты также получают бесплатную доставку в один прекрасный день, вместо того, чтобы добавить еще одно условие к чрезвычайно сложному условному заявлению, существенно удвоив количество модульных тестов, вы бы добавили, что условие для - это бесплатная доставка, что увеличится количество комбинаций для этого меньшего блока от 4 до 8, но это лучше, чем наличие функции, которая имеет 16 условий (т.е. 65536 комбинаций) до 17 условий (т.е. 131072 комбинаций).


На данный момент, если мы не знаем, и понять точный домен, мы можем только сделать общие предположения о реорганизации ваших классов и методов на более мелкие части. Кроме того, в то время как mart делает хорошую точку, используя длину строки> 5, не нуждающуюся в проверке для каждой строки длиной более 5, я думаю, что если у вас есть фактическое условие, уменьшенное до истинного/ложного, то необходимо выполнить совместные условия. Например: (String Length > 5) && B && C && D && E имеет 5 условий или 32 комбинации. Вы должны проверить все 32. Что вы не должны делать, это придумать 100 различных способов показать, что строка String Length> 5 истинна. Причина, по которой я говорю, чтобы проверить все 32 комбинации, заключается в том, что, хотя условия могут быть реорганизованы и протестированы, вы все же хотите проверить, используете ли вы A && B && (C || D), чтобы быть уверенным, что никто не сделал опечатку A && B || (C && D). Или, другими словами, проверка отдельных условий не гарантирует правильное кодирование комбинации этих условий.

0

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

Модульное тестирование не предназначено, чтобы охватить все варианты, на самом деле, если вы тестируете верно & & истина, ложь & & ложь, и так далее, вы практически тестирование & & оператора, а не логика, вы должны охватывать что делает ваша функция как «единица», а не все возможные комбинации входов, просто подумайте о функции, которая проверяет, превышает ли длина строки более 5, вы собираетесь создать все возможные строки в мире? или вы просто испытаете тот, который равен 5, один с более чем 5 символами, а тот, который ниже, возможно, вам тоже нужно проверить значение null, но не более того.

В любом случае, чтобы дать вам реальную альтернативу тому, что вы спрашиваете, можете ли вы добавить NUnit в свой проект, вы можете использовать https://github.com/nunit/docs/wiki/Combinatorial-Attribute, чтобы сгенерировать все те те тесты, которые вы хотите.