CodeCoverage vs ExpectedException - PullRequest
       17

CodeCoverage vs ExpectedException

8 голосов
/ 22 октября 2009

У меня есть несколько юнит-тестов этого паттерна:

[TestMethod ()]
[ExpectedException (typeof (ArgumentNullException))]
public void DoStuffTest_Exception ()
{
    var foo = new Foo ();
    Foo.DoStuff (null);
}

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

Подумав некоторое время над этой проблемой, лучшее решение, которое я мог придумать, это добавить попытку / улов. Поскольку это повторяющийся паттерн, я собираюсь создать вспомогательный метод в соответствии с

public static void ExpectException<_T> (Action action) where _T: Exception
{
    try { action(); }
    catch (_T) { return; }
    Assert.Fail ("Expected " + _T);
}

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

Это действительный дизайн, или я что-то упустил?

Редактировать: Ugs ... похоже, что вышеупомянутый метод ExpectException оставляет мне и 1 непокрытый блок.

Ответы [ 4 ]

10 голосов
/ 22 октября 2009

То, что вы предлагаете, действительно. Помимо вашей проблемы с покрытием кода, я бы сказал, что это лучше, чем использование атрибута ExpectedException, поскольку он явно показывает, какая строка теста может выдать исключение. Использование ExpectedException означает, что любая строка кода в тесте может выдать ожидаемый тип исключения, и тест все равно будет пройден. Если ошибка возникает из-за другого вызова, который не должен был генерировать, это может скрыть тот факт, что тест должен быть неудачным, потому что строка, которая должна выдавать, не является.

Что было бы полезным, если бы вы предложили изменить возвращаемое исключение:

public static _T ExpectException<_T> (Action action) where _T: Exception
{
    try { action(); }
    catch (_T ex) { return ex; }
    Assert.Fail ("Expected " + typeof(_T));
    return null;
}

Это позволило бы тестовому коду дополнительно утверждать исключение, если оно требуется (т. Е. Проверять, использовалось ли конкретное сообщение).

NUnit (хотя не похоже, что вы используете его, поскольку у вас есть атрибут TestMethod), имеет встроенную конструкцию, аналогичную предложенной вами:

Assert.Throws<ArgumentNullException>(() => Foo.DoStuff(null))
3 голосов
/ 17 сентября 2014

@ adrianbanks ExpectException не работает должным образом, если параметр действия вызывает другое исключение, кроме ожидаемого:

[TestMethod]
public void my_test()
{
    ExpectException<InvalidOperationException>(delegate()
    {
        throw new ArgumentException("hello");
    });
}

Когда я выполняю TestMethod "my_test", я только что получил сообщение о том, что метод теста повышен, и System.ArgumentException: hello. В этом случае он должен сказать «Expected InvalidOperationException». Я предлагаю новую версию для метода ExpectException:

public static void VerifierException<T>(Action action) where T : Exception
{
    try
    {
        action();
    }
    catch (Exception ex)
    {
        Assert.IsInstanceOfType(ex, typeof(T));
        return;
    }

    Assert.Fail("Aucune exception n'a été déclenchée alors qu'une exception du type " + typeof(T).FullName + " était attendue");
}
2 голосов
/ 08 декабря 2015

Я знаю, что это старая тема, но я столкнулся с той же проблемой.

В конце концов я спросил себя: зачем мне знать охват тестов? Не знаю! - Итак, давайте исключим их, чтобы охват был более чистым.

В моем тестовом проекте я добавил файл CodeCoverage.runsettings, и это содержимое:

<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
        <Configuration>
          <CodeCoverage>
            <ModulePaths>
              <Exclude>
                <ModulePath>.*tests.dll</ModulePath>
                <ModulePath>.*Tests.dll</ModulePath>
                <!-- Add more ModulePath nodes here. -->
              </Exclude>
            </ModulePaths>
          </CodeCoverage>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>

После выбора этого файла настроек теста файл мой охват кода составляет 100%

Таким образом, нет необходимости «взламывать» систему покрытия кода модульного теста, просто чтобы достичь 100%: -)

0 голосов
/ 22 октября 2009

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

...