NUnit, ни Assert.Throws, ни [ExpectedException] ловить выброшенное исключение

Прежде чем начать, я хочу прояснить, что я уже проверил решения как в этом вопросе, так и в этом вопросе.

Метод для тестирования

public static DataSet ExecuteDataSet(this SqlConnection connection, string sql)
{
    if (null == connection || null == sql)
    {
        throw new ArgumentNullException();
    }

    using (var command = connection.CreateCommand())
    {
        // Code elided for brevity
    }
}

Методы испытаний

[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void ExecuteDataSetThrowsForNullConnection()
{
    ((SqlConnection)null).ExecuteDataSet("SELECT TOP 1 * FROM prep");
}

[Test]
public void ExecuteDataSetThrowsForNullSql()
{
    Assert.Throws<ArgumentNullException>(
        () => Resource.Connection.ExecuteDataSet(null)
    );
}

Странное поведение

Ни одна из версий метода тестирования не улавливает ArgumentNullException бросается сразу после входа в ExecuteDataSet метод. Поток управления переходит на следующую строку (using (var command = connection.CreateCommand())) и NullReferenceException происходит вместо этого (что, конечно, не обрабатывается ни одним из моих тестовых случаев, потому что его никогда не следует выбрасывать).

Первоначально первый метод испытаний (ExecuteDataSetThrowsForNullConnection) выглядел так же, как второй (ExecuteDataSetThrowsForNullSql). когда Assert.Throws не удалось поймать исключение, я провел некоторое исследование и отметил, что некоторые люди рекомендовали использовать ExpectedException вместо. Я изменил тестовый код соответственно, но безрезультатно.

Напомним, что это 32-битный код.NET 3.5, протестированный под NUnit 2.5.9. Я использую TestDriven.NET для интеграции с Visual Studio, и у меня установлены последние версии NCover и NDepend.

TL; DR Вопрос

Почему тестовые методы не улавливают выброшенное исключение и как его исправить?

РЕДАКТИРОВАТЬ

Эта версия метода испытаний работает.

[Test]
public void ExecuteDataSetThrowsForNullConnection()
{
    try
    {
        ((SqlConnection)null).ExecuteDataSet("SELECT TOP 1 * FROM prep");
    }
    catch(ArgumentNullException e)
    {
        Assert.AreEqual(true, true);
    }
    catch (Exception e)
    {
        Assert.Fail("Expected ArgumentNullException, but {1} was thrown instead.", e.GetType().Name);
    }
}

1 ответ

Решение

Я предполагаю, что вы на самом деле не тестируете тот код, который считаете нужным. Попробуйте положить немного Console.WriteLine заявления и посмотреть, если они напечатаны. Если вы установите точку останова на throw и запустите тесты в отладчике, точка останова попадет? Если управление передается следующему оператору, это означает, что исключение никогда не генерируется - его невозможно перехватить способом, который позволил бы продолжить выполнение в методе throwing, если вы не обнаружили действительно странную ошибку CLR.

Я написал много такого кода, и в NUnit его никогда не было.

Кроме того, я считаю хорошей практикой включать имя параметра в ArgumentExceptions, поэтому я бы написал:

if (connection == null)
{
    throw new ArgumentNullException("connection");
}
if (sql == null)
{
    throw new ArgumentNullException("sql");
}

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

Другие вопросы по тегам