Как убедиться, что Debug.Assert правильно запускается с помощью NUnit - PullRequest
6 голосов
/ 10 февраля 2012

Я пытался обдумать эту проблему: как создать модульный тест, который проверяет, завершается ли функция из-за сбоя Debug.Assert (из System.Diagnostics), помечая его как пройденный, когда он это делает, икак не удалось.

Я знаю, NUnit имеет функцию [ExpectedException(typeof( ArgumentException ) )], но я не могу выяснить, что это за исключение на веб-сайте MSDN.Интуиция сказала бы, что это может быть что-то вроде AssertionException, и оно действительно существует ... но является частью инфраструктуры NUnit.Я думаю, что это исключение NUnit Assert бросить вокруг.Я мог бы просто обнулить его, используя:

[ExpectedException(typeof(Exception))]

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

Я думаю, что есть обходной путь замены метода Debug.Assert на NUnit аналог (яЯ все еще в начале своего проекта, так что это не слишком большой рефакторинг), но я предполагаю, что многие программисты придерживаются функциональности Debug.Assert, как это стандартно в .NET.

Как таковой, я хотел бы знать, как "утверждать" Debug.Assertion сбои без необходимости "убивать" экран отладки Windows из моего проекта?

Чтобы иметь конкретный примериз контракта в моем коде есть пример ниже.Для тех, кому это может показаться знакомым, это таблица To-Wound из настольной варгейма Warhammer 40K, написанная как функция.

static public int assaultToHit(int _attacker_WeaponSkill,
            int _defender_WeaponSkill)
        {
            //Preconditions
            Debug.Assert(_attacker_WeaponSkill >= 1 && _attacker_WeaponSkill <= 10,
                "Weapon Skill stat must be in range [1,10]");
            Debug.Assert(_defender_WeaponSkill >= 1 && _defender_WeaponSkill <= 10,
                "Weapon Skill stat must be in range [1,10]");

            int target;
            if (_attacker_WeaponSkill > _defender_WeaponSkill)
            {
                target=3;
            }
            else if (_defender_WeaponSkill >= (_attacker_WeaponSkill + _attacker_WeaponSkill + 1))
            {
                target=5;
            }
            else
            {
                target=4;
            }

            //postconditions
            Debug.Assert(target >= 3 && target <= 5,
                "To hit target for assault must be in range [3,5]");

            return target;
        }

Функция для проверки предварительных условий будет выглядеть примерно так:

    [TestCase(-1,2)]
    [TestCase(1, -2)]
    [TestCase(-1, -2)]
    [TestCase(11, 2)]
    [TestCase(1, 20)]
    [TestCase(11, 20)] 
    [ExpectedException(typeof(Exception))]
    public void testContract_AssaultToHit(int _attacker_weaponskill, 
        int _defender_weaponskill)
    {
        Warhammer40kRules.assaultToHit(_attacker_weaponskill, 
            _defender_weaponskill);
    }

Ответы [ 3 ]

5 голосов
/ 11 февраля 2012

Кажется, вы использовали неправильные средства для управления потоком приложений в случае ошибки.Debug.Assert() не должен использоваться для управления потоком логики приложения.

Модульный тест должен охватывать реальный тестовый пример, и кажется, что вы пытаетесь реализовать неправильный тестовый пример, или вам нужно выдать исключение / etc вместо использования Debug.Assert().Вы можете поделиться некоторым кодом, чтобы можно было дать некоторые конкретные советы.

В любом случае вы можете прочитать MSDN о том, как добавить пользовательский прослушиватель трассировки и перехватывать Assert вызовы.

Полезноссылки:

3 голосов
/ 13 февраля 2012

Начиная с https://stackoverflow.com/a/117247/605538 можно найти рекомендации по использованию исключений для открытого интерфейса, используя утверждения для проверки внутреннего кода.Когда юнит-тестирование предварительных условий функции (и аналогичная мысль может применяться для постусловий, хотя я лично предпочел бы использовать там утверждения), поэтому можно рекомендовать использовать исключения вместо использования утверждений.

Использование исключений вместо приведет к получению такого образца:

static public int assaultToHit(int _attacker_WeaponSkill,
        int _defender_WeaponSkill)
    {
        //Preconditions
        if(!(_attacker_WeaponSkill >= 1 && _attacker_WeaponSkill <= 10))
        {
            throw new ArgumentOutOfRangeException("Attackers WeaponSkill must be in range [1,10]");
        }
        if(!(_defender_WeaponSkill >= 1 && _defender_WeaponSkill <= 10))
        {
            throw new ArgumentOutOfRangeException("Defenders WeaponSkill must be in range [1,10]");
        }

        ...
        //rest unchanged
    }

В сочетании со следующим тестом NUnit:

[TestCase(-1,2)]
[TestCase(1, -2)]
[TestCase(-1, -2)]
[TestCase(11, 2)]
[TestCase(1, 20)]
[TestCase(11, 20)] 
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void testContract_AssaultToHit(int _attacker_weaponskill, 
    int _defender_weaponskill)
{
    Warhammer40kRules.assaultToHit(_attacker_weaponskill, 
        _defender_weaponskill);
}

Боковая панель [12 июня 2014 года]: Недавно я пришел к выводу, что вам не нужно проверять нарушения предварительных условий в контексте «Проектирование по контракту».Если вы проектируете с учетом DbC, вы в основном заявляете следующее: «Если вы вызываете метод и соответствуете его предварительным условиям, вам гарантировано определенное поведение. Если вы не соответствуете предварительным условиям метода, вы можете ожидать неопределенного поведения».В широком смысле слова «неопределенное поведение» это означает, что вы не можете ожидать какого-либо состояния.Вы можете получить исключения, возможно, вообще ничего не происходит, и, возможно, ваш жесткий диск отформатирован (ну ... вы поняли;)).Таким образом, вы не можете проверить «неопределенное поведение».

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

1 голос
/ 25 сентября 2012

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

Если Кодовые контракты слишком велики для вас, я бы порекомендовал вам заставить ваши контракты генерировать исключения, когда неявные контракты не соблюдаются, а не утверждаются.

Однако Debug.Asserts по-прежнему полезны, но их следует использовать в ситуациях, которые менее вероятны, например, после оператора, который делает вызов, возвращающий ноль, вместо ожидаемой пустой коллекции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...