Покрытие ветвей без ветвления — я знаю, звучит странно

#java #c# #unit-testing #testing #code-coverage

#java #c# #модульное тестирование #тестирование #покрытие кода

Вопрос:

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

 bool IsBroken()
{
    var isBroken = Check(..., "error-1")
        | Check(..., "error-2")
        ...
        | Check(..., "error-n");

    if (...)
    {
        isBroken |= Check(..., "error-1")
            | Check(..., "error-2")
            ...
            | Check(..., "error-n");
    }
    else
    {
        isBroken |= Check(..., "error-1")
            | Check(..., "error-2")
            ...
            | Check(..., "error-n");
    }

    // ...
    // Could have more if-else or even nested if-else

    return isBroken;
}

bool Check(bool condition, string message)
{
    if (condition)
    {
        errors.Add(message);
    }
    return condition;
}
  

Цель состоит в том, чтобы выполнить все проверки независимо от результата. Однако проблема с этим кодом заключается в том, что он не подходит для покрытия ветвей.

Простым решением было бы, очевидно, Check(bool, string) заменить тело функции, но это создает еще одну меньшую проблему, которая заключается в беспорядке кода. Я пытаюсь найти решение, которое может дать мне лучшее из обоих миров. Есть идеи ?!

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

1. learn.microsoft.com/en-us/dotnet/csharp/pattern-matching

2. isBroken на самом деле не требуется: return !errors.isEmpty()

3. Кроме того, можно использовать что-то вроде a List<Validator> и перебирать список, где каждый элемент списка инкапсулирует an if .

Ответ №1:

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

Пример кода:

 class A
{
    Dictionary<Func<A, bool>, string> dictOfChecks = new Dictionary<Func<A, bool>, string>()
    {
        //Example of checks                 , Error Message
        { (arg) => arg.ToString().Length < 3, "Length is smaller than 3" },
        { (arg) => arg.ToString().Length < 2, "Length is smaller than 2" },
    };

    public bool IsBroken()
    {
        //there you can enter multiple call of GetErrors to check whether there are some
        var errors = GetErrors(dictOfChecks);

        return errors.Length == 0;
    }

    public string[] GetErrors(Dictionary<Func<A, bool>, string> dict)
    {
        var errors = new List<string>();

        foreach (KeyValuePair<Func<A, bool>, string> kvp in dict)
        {
            if (kvp.Key.Invoke(this))
                errors.Add(kvp.Value);
        }

        return errors.ToArray();
    }
}

  

Ответ №2:

Придумал подходящее решение:

 _ = condition amp;amp; Append("error");

bool Append(string error)
{
    errors.Add(error);
    return true;
}
  

хотя это также можно записать как if (condition) Append("error") . В нашем случае операторы блока должны быть записаны как:

 if (condition)
{
    ...
}