Avatar ·

Как в MSTest проверить, что метод выбрасывает исключение?

Всем привет! Я новичок в юнит-тестировании на C# и использую MSTest. У меня есть метод, который при определенных условиях должен выбрасывать исключение. Например, вот такой простой метод деления: ```csharp public int Divide(int a, int b) { if (b == 0) throw new DivideByZeroException("Делить на ноль нельзя!"); return a / b; } ``` Я хочу написать тест, который проверяет, что при передаче b = 0 действительно выбрасывается DivideByZeroException. Но я не могу понять, как это правильно сделать с помощью Assert. Пробовал оборачивать вызов в try-catch и ставить Assert.Fail, но это выглядит очень коряво. Видел, что в других фреймворках (например, NUnit) есть специальный атрибут [ExpectedException], но в MSTest он, кажется, устарел или работает не так. Подскажите, пожалуйста, как правильно и красиво написать такой тест в MSTest? Может быть, есть какой-то специальный метод Assert для этого? Буду благодарен за пример кода. <<>>

Использование Assert.ThrowsException в MSTest

Начиная с MSTest v2 (который сейчас является стандартным), для проверки исключений существует удобный статический метод Assert.ThrowsException<T>. Он позволяет лаконично и читаемо проверить, что код выбрасывает ожидаемое исключение. Более того, этот метод возвращает сам объект исключения, что позволяет дополнительно проверить его свойства (например, сообщение).

Вот как будет выглядеть ваш тест для метода Divide:

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class MathTests
{
    [TestMethod]
    public void Divide_DivideByZero_ThrowsDivideByZeroException()
    {
        // Arrange
        var calculator = new Calculator(); // предполагаем, что Divide - это метод класса Calculator

        // Act & Assert
        // Assert.ThrowsException вернет исключение, если оно было выброшено,
        // или провалит тест, если исключения не было или оно другого типа.
        var ex = Assert.ThrowsException<DivideByZeroException>(() => calculator.Divide(10, 0));

        // Дополнительно можно проверить сообщение исключения
        Assert.AreEqual("Делить на ноль нельзя!", ex.Message);
    }
}

Как это работает?

  • Лямбда-выражение: () => calculator.Divide(10, 0) – это делегат Action, который содержит код, который, как ожидается, выбросит исключение.
  • Проверка типа: Если код в лямбде выбрасывает исключение типа DivideByZeroException (или производного от него), тест проходит. Если исключение не выбрасывается или выбрасывается исключение другого типа (например, Exception), тест падает с понятным сообщением.
  • Возвращаемое значение: Метод возвращает перехваченное исключение, что позволяет проверить его свойства (Message, InnerException, Data и т.д.).

Что делать, если нужно проверить, что исключение НЕ выбрасывается?

Для этого не нужен специальный Assert. Просто вызовите метод без обертки в ThrowsException. Если исключение будет выброшено, тест сам упадет:

[TestMethod]
public void Divide_ValidInput_ReturnsResult()
{
    // Arrange
    var calculator = new Calculator();

    // Act
    var result = calculator.Divide(10, 2);

    // Assert
    Assert.AreEqual(5, result);
    // Если бы Divide выбросил исключение, тест бы не дошел до этой строки и упал бы.
}

Важные замечания для начинающих

  • Убедитесь, что у вас MSTest v2. Если вы используете старый MSTest v1 (из .NET Framework), то Assert.ThrowsException может отсутствовать. В этом случае обновите пакет NuGet: Microsoft.NET.Test.Sdk и MSTest.TestAdapter/MSTest.TestFramework.
  • Не используйте [ExpectedException] – этот атрибут устарел и не рекомендуется, так как он не позволяет точно указать, в какой строке должно произойти исключение, и не дает проверить его свойства.
  • Проверяйте точный тип исключения. Если ваш метод выбрасывает ArgumentException, а вы проверяете на Exception, тест пройдет, но это плохая практика. Всегда проверяйте самый конкретный тип.

Использование Assert.ThrowsException делает тесты чистыми, выразительными и соответствующими современным стандартам юнит-тестирования в MSTest.

<<>>

Альтернатива: ручная проверка через try-catch (для старых версий MSTest)

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

[TestMethod]
public void Divide_DivideByZero_ThrowsDivideByZeroException_Manual()
{
    // Arrange
    var calculator = new Calculator();

    // Act
    try
    {
        calculator.Divide(10, 0);
        // Если мы дошли до этой строки, исключение не было выброшено
        Assert.Fail("Ожидалось исключение DivideByZeroException, но его не было.");
    }
    catch (DivideByZeroException ex)
    {
        // Исключение перехвачено - тест пройден.
        // Дополнительно можно проверить сообщение
        Assert.AreEqual("Делить на ноль нельзя!", ex.Message);
    }
    catch (Exception)
    {
        // Если выброшено исключение другого типа - тест тоже падает
        Assert.Fail("Выброшено исключение неверного типа.");
    }
}

Недостатки этого подхода:

  • Много шаблонного кода, который отвлекает от сути теста.
  • Легко забыть вызвать Assert.Fail или неправильно обработать catch-блоки.
  • Метод Assert.ThrowsException делает то же самое, но короче и надежнее.

Поэтому рекомендуется по возможности обновить MSTest до v2 и использовать встроенный метод.