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

StackOverflow https://stackoverflow.com/questions/113395

Вопрос

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()

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

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

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


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

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

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[Test, 
    ExpectedException(typeof(System.ArgumentException),
    ExpectedException=ProductExceptionMessages.DuplicateProductName)]
public void TestCreateDuplicateProduct()
{
    _repository.CreateProduct("TestCreateDuplicateProduct");
    _repository.CreateProduct("TestCreateDuplicateProduct");
} 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top