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

Тест Visual Studio может проверять ожидаемые исключения, используя атрибут 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()

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

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

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


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

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

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

Ответ 1

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

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

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

Как и для нескольких исключений, я из земли С++, где создание нагрузок и множеств исключений (до одного выражения "за один"!) в больших искажениях приемлемо (если не общее), но .Net-метаданные системе, вероятно, это не нравится, следовательно, этот совет.

Ответ 2

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

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);
}

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

[Fact]
public void TestException()
{
   Exception ex = Record.Exception(() => myClass.DoSomethingExceptional());
   // Assert whatever you like about the exception here.
}

Ответ 6

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

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

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

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

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

Ответ 7

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

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

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

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

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

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

[Test, 
    ExpectedException(typeof(System.ArgumentException),
    ExpectedException=ProductExceptionMessages.DuplicateProductName)]
public void TestCreateDuplicateProduct()
{
    _repository.CreateProduct("TestCreateDuplicateProduct");
    _repository.CreateProduct("TestCreateDuplicateProduct");
}