Может ли мой класс модульного тестирования отслеживать выполнение теста - PullRequest
0 голосов
/ 13 июля 2020

Наш код определяет некоторые «правила» в коллекции List<Rule>. Каждое правило содержит несколько лог c в виде строки. Все правила передаются в «механизм правил» вместе с некоторыми данными. Механизм правил последовательно оценивает данные по правилам, пока не найдет правило, которое оценивается как истинное, а затем возвращает Rule.

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

Как мне написать тест, который говорит: «Убедитесь, что каждое правило оценивается как истинное по крайней мере в одном модульном тесте "?

Я придумал способ запустить некоторый код разрыва устройства после выполнения всех тестов (см. https://xunit.github.io/docs/shared-context.html#class -fixture ), и используя переменную stati c для записи оцененных правил, я могу проверить во время разборки, все ли правила были возвращены во время модульных тестов. Но у этого подхода есть нежелательный эффект, заключающийся в том, что он приводит к тому, что отдельный тест сообщается как неуспешный (при разборке), что на самом деле не было ошибкой.

1 Ответ

0 голосов
/ 13 июля 2020

TL; DR: контролируйте последовательность тестирования. Поскольку это интеграционные тесты, это не большое «нет-нет», если бы они были модульными тестами.

Я обнаружил, что, используя переменную stati c для записи правил, которые оцениваются как истинные, и обеспечивая последний тест have-all-rules-as-true , согласно совету здесь https://hamidmosalla.com/2018/08/16/xunit-control-the-test-execution-order/, становится тривиально легко достичь того, что мне нужно.

В случае, если ссылка умирает, вот как реализовать этот подход

Сначала создайте AlphabeticalOrderer, реализующий xUnit ITestCaseOrderer:

public class AlphabeticalOrderer : ITestCaseOrderer
{
    public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases)
            where TTestCase : ITestCase
    {
        var result = testCases.ToList();
        result.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name));
        return result;
    }
}

Затем используйте вновь созданный атрибут на свой тестовый класс и создайте переменную stati c для записи прогресса:

[TestCaseOrderer("MyCompany.AlphabeticalOrderer", "MyTestsDLL")]
public class RulesTests
{
    private static List<Rule> _untestedRules;

    public RulesTests()
    {
        if (_untestedRules == null) // this will run once per [Fact]
        {
            _untestedRules = <some way of getting all the rules> // this will only run first time around
        }
    }

Каждый раз, когда срабатывает правило, запишите, какое это было правило:

    private void MarkRuleAsTested(LendingRule testedRule)
    {
        var rulesToRemove = _untestedRules.Where(r => r.Logic == testedRule.Logic).ToList();

        foreach (var ruleToRemove in rulesToRemove)
        {
            _untestedRules.Remove(ruleToRemove);
        }
    }

Затем последний тест для запуска следует проверить коллекцию:

    [Fact]
    public void ZZ_check_all_rules_have_been_triggered_by_other_tests()
    {
        // This test must run last because it validates that the
        // OTHER tests taken together have resulted in each rule
        // being evaluated as true at least once.
        if (!_untestedRules.Any())
        {
            return;
        }

        var separator = "\r\n------------------------\r\n";
        var untestedRules = string.Join(separator, _untestedRules.Select(ur => $"Logic: {ur.Logic}"));
        throw new SomeRulesUntestedException($"The following rules have not been tested to resolve as True: {separator}{untestedRules}{separator}");
    }

Это приводит к хорошо читаемой ошибке теста.

Вы также можете начать с пустой коллекции c _testedRules и конец сравните этот набор с полным набором правил.

...