В MSTest, Как я могу проверить точное сообщение об ошибке, используя [ExpectedException (typeof (ApplicationException))] - PullRequest
35 голосов
/ 22 декабря 2009

Используя MSTest, как я могу проверить точное сообщение об ошибке, полученное тестовым методом? Я знаю, [ExpectedException(typeof(ApplicationException), error msg)] не сравнивает сообщение об ошибке, полученное от моего метода тестирования, хотя в других рамках модульного тестирования это происходит.

Одним из способов решения этой проблемы является написание моего модульного теста с использованием некоторого блока try catch, но мне снова нужно написать еще 4 строки.

Есть ли самый умный способ проверить сообщение об ошибке.

Ура, Pritam

Ответы [ 10 ]

35 голосов
/ 05 февраля 2012

Вы можете создать свой собственный атрибут ExpectedException , где вы можете Assert сообщение Exception, которое было брошено.

Код

namespace TestProject
{
    public sealed class MyExpectedException : ExpectedExceptionBaseAttribute
    {
        private Type _expectedExceptionType;
        private string _expectedExceptionMessage;

        public MyExpectedException(Type expectedExceptionType)
        {
            _expectedExceptionType = expectedExceptionType;
            _expectedExceptionMessage = string.Empty;
        }

        public MyExpectedException(Type expectedExceptionType, string expectedExceptionMessage)
        {
            _expectedExceptionType = expectedExceptionType;
            _expectedExceptionMessage = expectedExceptionMessage;
        }

        protected override void Verify(Exception exception)
        {
            Assert.IsNotNull(exception);

            Assert.IsInstanceOfType(exception, _expectedExceptionType, "Wrong type of exception was thrown.");

            if(!_expectedExceptionMessage.Length.Equals(0))
            {
                Assert.AreEqual(_expectedExceptionMessage, exception.Message, "Wrong exception message was returned.");
            }
        }
    }
}

Использование

[TestMethod]
[MyExpectedException(typeof(Exception), "Error")]
public void TestMethod()
{
    throw new Exception("Error");
}
15 голосов
/ 22 декабря 2009

Используйте этот маленький вспомогательный класс:

public static class ExceptionAssert
{
    public static void Throws<TException>(Action action, string message)
        where TException : Exception
    {
        try
        {
            action();

            Assert.Fail("Exception of type {0} expected; got none exception", typeof(TException).Name);
        }
        catch (TException ex)
        {
            Assert.AreEqual(message, ex.Message);
        }
        catch (Exception ex)
        {
            Assert.Fail("Exception of type {0} expected; got exception of type {1}", typeof(TException).Name, ex.GetType().Name);               
        }
    }
}

Использование:

Foo foo = new Foo();
foo.Property = 42;

ExceptionAssert.Throws<InvalidOperationException>(() => foo.DoSomethingCritical(), "You cannot do anything when Property is 42.");

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

6 голосов
/ 02 сентября 2011

Свободные утверждения ( NuGet ) имеет очень естественный синтаксис для определения ожиданий в модульных тестах:

objectundertest.Invoking(o => o.MethodUnderTest()).ShouldThrow<ExpectedException>()
    .WithMessage("the expected error message");

Существует несколько вариантов проверки сообщения об ошибке любым алгоритмом (Where(e => ...), а также проверки внутренних исключений и их сообщений.

5 голосов
/ 21 декабря 2011

Я искал способ проверить наличие и тип внутреннего исключения с помощью mstest, и я нашел этот вопрос. Я знаю эту тему 2 года назад, но так как моего решения здесь нет, позвольте мне поделиться им.

Для меня самый элегантный способ решения проблемы - создать производный атрибут, вот мой (извините, но комментарии и строки на французском, мой естественный язык, но это должно быть очевидно):

#region Références
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Text.RegularExpressions;
#endregion

namespace MsTestEx
{
    /// <summary>
    /// Extention de l'attribut ExpectedException permettant de vérifier plus d'éléments (Message, InnerException, ...)
    /// </summary>
    public class ExpectedExceptionEx
        : ExpectedExceptionBaseAttribute
    {
        #region Variables locales
        private Type    _ExpectedException              = null;
        private string  _ExpectedMessage                = null;
        private Type    _ExpectedInnerException         = null;
        private string  _ExpectedInnerExceptionMessage  = null;
        private bool    _IsExpectedMessageRegex         = false;
        private bool    _IsExpectedInnerMessageRegex    = false;
        private bool    _AllowDerivedType               = false;
        private bool    _AllowInnerExceptionDerivedType = false;

        private bool    _CheckExpectedMessage           = false;
        private bool    _CheckInnerExceptionType        = false;
        private bool    _CheckInnerExceptionMessage     = false;
        #endregion

        #region Propriétés
        /// <summary>
        /// Vérifie que le message de l'exception correspond à celui-ci.
        /// </summary>
        public string ExpectedMessage
        {
            get { return _ExpectedMessage; }
            set { _ExpectedMessage = value; _CheckExpectedMessage = true; }
        }

        /// <summary>
        /// Vérifie que le message de l'inner-exception correspond à celui-ci.
        /// </summary>
        public string ExpectedInnerExceptionMessage
        {
            get { return _ExpectedInnerExceptionMessage; }
            set { _ExpectedInnerExceptionMessage = value; _CheckInnerExceptionMessage = true; }
        }

        /// <summary>
        /// Vérifie que l'exception possède bien une inner-exception du type spécifié.
        /// Spécifier "null" pour vérifier l'absence d'inner-exception.
        /// </summary>
        public Type ExpectedInnerException
        {
            get { return _ExpectedInnerException; }
            set { _ExpectedInnerException = value; _CheckInnerExceptionType = true; }
        }

        /// <summary>
        /// Indique si le message attendu est exprimé via une expression rationnelle.
        /// </summary>
        public bool IsExpectedMessageRegex
        {
            get { return _IsExpectedMessageRegex; }
            set { _IsExpectedMessageRegex = value; }
        }

        /// <summary>
        /// Indique si le message attendu de l'inner-exception est exprimé via une expression rationnelle.
        /// </summary>
        public bool IsExpectedInnerMessageRegex
        {
            get { return _IsExpectedInnerMessageRegex; }
            set { _IsExpectedInnerMessageRegex = value; }
        }

        /// <summary>
        /// Indique si les exceptions dérivées sont acceptées.
        /// </summary>
        public bool AllowDerivedType
        {
            get { return _AllowDerivedType; }
            set { _AllowDerivedType = value; }
        }

        /// <summary>
        /// Indique si les inner-exceptions dérivées sont acceptées.
        /// </summary>
        public bool AllowInnerExceptionDerivedType
        {
            get { return _AllowInnerExceptionDerivedType; }
            set { _AllowInnerExceptionDerivedType = value; }
        }
        #endregion

        #region Constructeurs
        /// <summary>
        /// Indique le type d'exception attendu par le test.
        /// </summary>
        /// <param name="expectedException">Type de l'exception attendu.</param>
        public ExpectedExceptionEx(Type expectedException)
        {
            _ExpectedException = expectedException;
        }
        #endregion

        #region Méthodes
        /// <summary>
        /// Effectue la vérification.
        /// </summary>
        /// <param name="exception">Exception levée.</param>
        protected override void Verify(Exception exception)
        {
            Assert.IsNotNull(exception); // Pas eu d'exception, ce n'est pas normal

            // Vérification du type de l'exception
            Type actualType = exception.GetType();
            if (_AllowDerivedType) Assert.IsTrue(_ExpectedException.IsAssignableFrom(actualType), "L'exception reçue n'est pas du type spécifié ni d'un type dérivé.");
            else Assert.AreEqual(_ExpectedException, actualType, "L'exception reçue n'est pas du type spécifié.");

            // Vérification du message de l'exception
            if (_CheckExpectedMessage)
            {
                if (_IsExpectedMessageRegex)
                    Assert.IsTrue(Regex.IsMatch(exception.Message, _ExpectedMessage), "Le message de l'exception ne correspond pas à l'expression rationnelle");
                else
                {
                    string s1, s2;
                    if (exception.Message.Length > _ExpectedMessage.Length)
                    {
                        s1 = exception.Message;
                        s2 = _ExpectedMessage;
                    }
                    else
                    {
                        s1 = _ExpectedMessage;
                        s2 = exception.Message;
                    }
                    Assert.IsTrue(s1.Contains(s2), "Le message de l'exception ne contient pas et n'est pas contenu par le message attendu.");
                }
            }

            if (_CheckInnerExceptionType)
            {
                if (_ExpectedInnerException == null) Assert.IsNotNull(exception.InnerException);
                else
                {
                    // Vérification du type de l'exception
                    actualType = exception.InnerException.GetType();
                    if (_AllowInnerExceptionDerivedType) Assert.IsTrue(_ExpectedInnerException.IsAssignableFrom(actualType), "L'inner-exception reçue n'est pas du type spécifié ni d'un type dérivé.");
                    else Assert.AreEqual(_ExpectedInnerException, actualType, "L'inner-exception reçue n'est pas du type spécifié.");
                }
            }

            if (_CheckInnerExceptionMessage)
            {
                Assert.IsNotNull(exception.InnerException);
                if (_IsExpectedInnerMessageRegex)
                    Assert.IsTrue(Regex.IsMatch(exception.InnerException.Message, _ExpectedInnerExceptionMessage), "Le message de l'exception ne correspond pas à l'expression rationnelle");
                else
                {
                    string s1, s2;
                    if (exception.InnerException.Message.Length > _ExpectedInnerExceptionMessage.Length)
                    {
                        s1 = exception.InnerException.Message;
                        s2 = _ExpectedInnerExceptionMessage;
                    }
                    else
                    {
                        s1 = _ExpectedInnerExceptionMessage;
                        s2 = exception.InnerException.Message;
                    }
                    Assert.IsTrue(s1.Contains(s2), "Le message de l'inner-exception ne contient pas et n'est pas contenu par le message attendu.");
                }
            }
        }
        #endregion
    }
}

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

4 голосов
/ 22 декабря 2009

В MSTest нет встроенного способа сделать это. Это примерно так же «элегантно»:

[TestMethod]
public void Test8()
{
    var t = new Thrower();
    try
    {
        t.DoStuffThatThrows();
        Assert.Fail("Exception expected.");
    }
    catch (InvalidOperationException e)
    {
        Assert.AreEqual("Boo hiss!", e.Message);
    }
}

Однако вы могли бы подумать о переносе API Assert.Throws из xUnit.NET в пользовательскую библиотеку - это то, что мы сделали.

Вы также можете перейти в Microsoft Connect для голосования по этому предложению .

3 голосов
/ 23 июня 2018

MSTest v2 поддерживает Assert.Throws и Assert.ThrowsAsync, который возвращает захваченное исключение.

Вот статья о том, как перейти на MSTest v2: https://blogs.msdn.microsoft.com/devops/2017/09/01/upgrade-to-mstest-v2/

Вот пример использования:

var myObject = new MyObject();
var ex = Assert.Throws<ArgumentNullException>(() => myObject.Do(null));
StringAssert.Contains(ex.Message, "Parameter name: myArg");
2 голосов
/ 04 января 2018

Раздражение с аннотациями и блоками try / catch заключается в том, что между этапами ACT и ASSERT теста нет четкого разделения. Более простой метод состоит в том, чтобы «захватить» исключение как часть фазы ACT, используя процедуру обеспечения точности, такую ​​как:

public static class Catch
{
    public static Exception Exception(Action action)
    {
        Exception exception = null;

        try
        {
            action();
        }
        catch (Exception ex)
        {
            exception = ex;
        }

        return exception;
    }
}

Это позволяет вам:

// ACT
var actualException = Catch.Exception(() => DoSomething())

// ASSERT
Assert.IsNotNull(actualException, "No exception thrown");
Assert.IsInstanceOfType(actualException, expectedType);
Assert.AreEqual(expectedExceptionMessage, actualException.Message);
0 голосов
/ 22 декабря 2009

MbUnit также может сделать это:

[Test]
[Row(ExpectedExceptionMessage="my message")]
void TestBlah(...
0 голосов
/ 22 декабря 2009

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

Попробуйте проект расширения от Callum Hibbert и посмотрите, работает ли он.

Старый ответ:

Вы можете сделать это с NUnit 2.4 и выше. См. документацию Ожидаемого исключения здесь

[ExpectedException( typeof( ArgumentException), ExpectedMessage="unspecified", MatchType=MessageMatch.Contains )]
public void TestMethod()
{
...

MatchType может быть Exact (по умолчанию), Contains или Regex .., который в значительной степени обрабатывает сценарий использования 80%. Существует также усовершенствованный метод метода обработчика исключений, если проверка становится слишком сложной ... никогда не использовала ее лично ... она еще не нужна.

0 голосов
/ 22 декабря 2009

С MSTest вы не можете этого сделать.

Вы уже знаете решение этой проблемы: подтвердите сообщение об исключении в блоке перехвата.

...