Как проверить наличие ожидаемого исключения с конкретным сообщением об исключении из файла ресурсов в Visual Studio Test? - PullRequest
39 голосов
/ 22 сентября 2008

Visual Studio Test может проверять наличие ожидаемых исключений, используя атрибут ExpectedException. Вы можете передать исключение следующим образом:

[TestMethod]
[ExpectedException(typeof(CriticalException))]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

Вы также можете проверить сообщение, содержащееся в ExpectedException, например:

[TestMethod]
[ExpectedException(typeof(CriticalException), "An error occured")]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

Но при тестировании приложений I18N я бы использовал файл ресурсов для получения этого сообщения об ошибке (любой может даже решить проверить различные локализации сообщения об ошибке, если я хочу, но Visual Studio не позволит мне сделать это:

[TestMethod]
[ExpectedException(typeof(CriticalException), MyRes.MultipleOrganisationsNotAllowed)]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

Компилятор выдаст следующую ошибку:

Аргумент атрибута должен быть константное выражение, выражение типа или выражение для создания массива атрибут

Кто-нибудь знает, как проверить исключение, содержащее сообщение из файла ресурсов?


Один вариант, который я рассмотрел, - это использование пользовательских классов исключений, но на основе часто встречающихся советов, таких как:

"Создавайте и выбрасывайте пользовательские исключения если у вас есть условие ошибки, может быть программно обработан в иначе, чем любой другой существующий исключение. В противном случае, бросьте один из существующие исключения. " Источник

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

Ответы [ 7 ]

63 голосов
/ 22 сентября 2008

Я бы рекомендовал использовать вспомогательный метод вместо атрибута. Примерно так:

public static class ExceptionAssert
{
  public static T Throws<T>(Action action) where T : Exception
  {
    try
    {
      action();
    }
    catch (T ex)
    {
      return ex;
    }
    Assert.Fail("Exception of type {0} should be thrown.", typeof(T));

    //  The compiler doesn't know that Assert.Fail
    //  will always throw an exception
    return null;
  }
}

Тогда вы можете написать свой тест примерно так:

[TestMethod]
public void GetOrganisation_MultipleOrganisations_ThrowsException()
{
  OrganizationList organizations = new Organizations();
  organizations.Add(new Organization());
  organizations.Add(new Organization());

  var ex = ExceptionAssert.Throws<CriticalException>(
              () => organizations.GetOrganization());
  Assert.AreEqual(MyRes.MultipleOrganisationsNotAllowed, ex.Message);
}

Это также имеет то преимущество, что он проверяет, что исключение выдается на строке, которую вы ожидали, что оно будет сгенерировано, а не где-либо в вашем методе тестирования.

14 голосов
/ 16 января 2009

Аргумент ExpectedException Message не совпадает с сообщением об исключении. Это скорее сообщение, которое выводится в результатах теста, если ожидаемое исключение фактически не произошло.

7 голосов
/ 22 сентября 2008

Просто мнение, но я бы сказал, текст ошибки:

  • является частью теста, и в этом случае получение его из ресурса будет «неправильным» (в противном случае вы можете получить постоянно искаженный ресурс), поэтому просто обновите тест, когда вы измените ресурс (или тест не пройден )
  • не является частью теста, и вам следует только позаботиться о том, чтобы он выдал исключение.

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

Что касается множественных исключений, я из C ++, где создание нагрузок и нагрузок исключений (с точностью до одного оператора 'throw'!) В больших иерархиях допустимо (если не распространено), но метаданные .Net системе, вероятно, это не нравится, поэтому этот совет.

4 голосов
/ 22 сентября 2008

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

3 голосов
/ 22 сентября 2008

Если вы переключитесь на использование очень хорошей библиотеки xUnit.Net , вы можете заменить [ExpectedException] на что-то вроде этого:

[Fact]
public void TestException()
{
   Exception ex = Record.Exception(() => myClass.DoSomethingExceptional());
   // Assert whatever you like about the exception here.
}
1 голос
/ 22 сентября 2008

Интересно, движется ли NUnit по пути от простоты ... но вот, пожалуйста.

Новые улучшения (2.4.3 и более поздние?) Для атрибута ExpectedException позволяют вам больше контролировать проверки, которые будут выполняться для ожидаемого исключения с помощью метода Handler . Подробнее на официальной странице NUnit doc .. в конце страницы.

[ExpectedException( Handler="HandlerMethod" )]
public void TestMethod()
{
...
}

public void HandlerMethod( System.Exception ex )
{
...
}

Примечание. Что-то здесь не так. Почему ваши сообщения об исключениях интернационализированы? Используете ли вы исключения для вещей, которые необходимо обработать или уведомить пользователя. Если у вас нет кучки разнородных в культурном отношении разработчиков, исправляющих ошибки ... вам это не нужно. Достаточно исключений на английском или общепринятом языке. Но если вам нужно это ... это возможно:)

0 голосов
/ 19 февраля 2009

Я столкнулся с этим вопросом, пытаясь решить подобную проблему самостоятельно. (Я подробно опишу решение, на котором остановился ниже.)

Я должен согласиться с комментариями Гишу об интернационализации сообщений об исключениях, являющихся запахом кода.

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

В какой-то момент я рассмотрел (и протестировал) использование блоков try / catch, чтобы избежать требования к константе атрибутом ExpectedException, но это выглядело так, как будто это привело бы к большому количеству дополнительного кода при применении в большом масштабе .

В конце концов, решение, на котором я остановился, состояло в том, чтобы создать статический класс в моей библиотеке ресурсов и сохранить в нем мои сообщения об исключениях. Таким образом, нет необходимости их интернационализировать (что, я согласен, не имеет смысла), и они становятся доступными в любое время, когда будет доступна строка ресурса, поскольку они находятся в одном пространстве имен. (Это соответствует моему желанию не делать проверку текста исключения сложным процессом.)

Мой тестовый код просто сводится к (простите за искажение ...):

[Test, 
    ExpectedException(typeof(System.ArgumentException),
    ExpectedException=ProductExceptionMessages.DuplicateProductName)]
public void TestCreateDuplicateProduct()
{
    _repository.CreateProduct("TestCreateDuplicateProduct");
    _repository.CreateProduct("TestCreateDuplicateProduct");
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...