Assert를 사용하여 예외가 발생했는지 어떻게 확인합니까?


830

Assert예외가 발생했는지 확인 하려면 어떻게해야합니까 (또는 다른 테스트 클래스?)?


어떤 단위 테스트 프레임 워크를 사용하고 있습니까?
Kevin Pullin

3
Visual Studio 통합
Alex

4
ExpectedException 속성이 도움이되지 않습니까? 참조 : msdn.microsoft.com/en-us/library/…
shahkalpesh

2
웃긴, 나는 이것에 대한 답을 찾기를 끝내고 stackoverflow.com/questions/741029/testing-exceptions 에서 찾았습니다 .
dfjacobs

답변:


978

"Visual Studio Team Test"의 경우 ExpectedException 특성을 테스트 방법에 적용합니다.

여기 문서의 샘플 : Visual Studio Team Test를 사용한 단위 테스트 연습

[TestMethod]
[ExpectedException(typeof(ArgumentException),
    "A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
   LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}

25
위의 ExpectedException 속성은 NUnit에서도 작동합니다 (그러나 [TestMethod]는 [Test] 여야 함).
dbkk 2016 년

5
@dbkk : NUnit에서 정확히 동일하게 작동하지 않습니다. 메시지는 예외 메시지를 matcvh해야하는 문자열로 취급됩니다 (그리고 IU가 더 의미가 있다고 생각합니다)
Ruben Bartelink

29
이 속성은 작업을 수행하며 C # 프로그래머를위한 기본 제공 기능이지만 충분히 유연하지 않기 때문에 사용하지 않는 것이 좋습니다. 테스트 설정 코드에 의해 예외 유형이 발생하면 어떻게되는지 생각해보십시오 : 테스트는 통과했지만 실제로 예상 한 것은 수행하지 않았습니다. 또는 예외 객체의 상태를 테스트하려면 어떻게해야합니까? 일반적으로 전체 메시지를 테스트하는 대신 StringAssert.Contains (e.Message ...)를 사용하고 싶습니다. 다른 답변에서 설명한대로 어설 션 방법을 사용하십시오.
steve

3
NUnit에서는 ExpectedException을 사용하지 마십시오. NUnit 3.0에서는 삭제됩니다. 내가 사용 Assert.Throws () <SpecificException> 선호
테렌스

5
MsTest 내에서 Assert.ThrowsException <T> 및 Assert.ThrowsExceptionAsync <T>를 사용할 수 있습니다.
고팔 크리슈 난

257

일반적으로 테스트 프레임 워크에 이에 대한 답변이 있습니다. 그러나 융통성이 충분하지 않으면 언제든지 다음을 수행 할 수 있습니다.

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }

@Jonas가 지적했듯이 이것은 기본 예외를 잡기 위해 작동하지 않습니다.

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // raises AssertionException
} catch (Exception) {
    // Catches the assertion exception, and the test passes
}

Exception을 반드시 잡아야하는 경우 Assert.Fail ()을 다시 발생시켜야합니다. 그러나 실제로, 이것은 당신이 이것을 손으로 쓰면 안된다는 신호입니다. 테스트 프레임 워크에서 옵션을 확인하거나 테스트를 위해 더 의미있는 예외를 던질 수 있는지 확인하십시오.

catch (AssertionException) { throw; }

어떤 종류의 예외를 잡아야 하는지를 포함하여 원하는 방식으로이 방법을 적용 할 수 있어야합니다. 특정 유형 만 예상하면 다음 catch과 같이 블록을 마무리하십시오 .

} catch (GoodException) {
} catch (Exception) {
    // not the right kind of exception
    Assert.Fail();
}

20
+1, 예외 유형 이외의 어설 션을 만들어야하는 경우 속성 대신이 방법을 사용합니다. 예를 들어, 예외 인스턴스의 특정 필드가 특정 값으로 설정되어 있는지 확인해야하는 경우
Pavel Repin 2016 년

2
오류 메시지를 지정할 필요는 없습니다. 이것으로 충분합니다 : [ExpectedException (typeof (ArgumentException))]
mibollma

5
이 솔루션이 최고라고 생각합니다. 테스트가 간단하다면 [ExpectedException (typeof (ArgumentException))]에 사용되지만, 제 생각에는 게으른 솔루션이며 편안하게 함정에 빠질 수 있습니다. 이 솔루션을 사용하면보다 정확한 테스트를 수행 할 수있는 특정 제어 기능을 제공 할 수있을뿐만 아니라 테스트 실행 보고서에 테스트 Writeline을 작성하여 예외가 예상대로 처리 될 수 있습니다.
evilfish

12
Assert.Fail ()이 예외를 발생 시키므로주의하십시오.
조나스

4
@ Vinnyq12 의미하는 것은 위 예제의 첫 번째 테스트는 절대 실패하지 않을 것입니다. 예외가 발생하면 (ExpectedExceptionAttribute에 의해 "catch"가 아닌) 테스트 실패
Jonas

113

이것을 구현하기 위해 내가 선호하는 방법은 Throws라는 메소드를 작성하고 다른 Assert 메소드와 마찬가지로 사용하는 것입니다. 불행히도 .NET에서는 정적 확장 메서드를 작성할 수 없으므로이 메서드는 마치 Assert 클래스의 빌드에 속하는 것처럼 사용할 수 없습니다. MyAssert 또는 이와 유사한 다른 것을 만드십시오. 수업은 다음과 같습니다.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        public static void Throws<T>( Action func ) where T : Exception
        {
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }

            if ( !exceptionThrown )
            {
                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but not thrown", typeof(T))
                    );
            }
        }
    }
}

즉, 단위 테스트는 다음과 같습니다.

[TestMethod()]
public void ExceptionTest()
{
    String testStr = null;
    MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}

나머지 단위 테스트 구문과 훨씬 비슷하게 보이고 동작합니다.


1
bool 플래그를 제거하고 더 작은 구현을 위해 호출 직후에 줄을 던지십시오.
gt

11
이를 개선하는 유일한 방법은 예외의 속성과 같은 것이 올바른지 계속 주장 할 수 있도록 함수가 포착 된 예외를 리턴하도록하는 것입니다.
Mark Hildreth

2
감사! 하나의 방법으로 여러 예외를 테스트하는 짧은 방법이기 때문에 이것은 나에게 가장 좋은 접근법 인 것 같습니다. 또한 훨씬 더 읽기 쉽습니다.
David Sherret

2
@MickeyPerlstein 속성은 테스트에 대한 AAA 규칙을 위반합니다. 특히, 법에 도달하기 전에 어레인지가 예외를 던지면 테스트는 ... eek!
freedomn-m 2016

2
V2 지원 - 마이크로 소프트는 마침내 MSTEST 업데이트에 라운드를 가지고 Assert.ThrowsException<T>Assert.ThrowsExceptionAsync<T>- 볼 blogs.msdn.microsoft.com/visualstudioalm/2017/02/25/...
Quango

62

NUNIT을 사용하면 다음과 같이 할 수 있습니다.

Assert.Throws<ExpectedException>(() => methodToTest());


추가로 확인하기 위해 발생 된 예외를 저장할 수도 있습니다.

ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);

참조 : http://nunit.org/docs/2.5/exceptionAsserts.html


60

원래 ExpectedException속성 이 없었던 MSTest를 사용하는 경우 다음을 수행 할 수 있습니다.

try 
{
    SomeExceptionThrowingMethod()
    Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
    Assert.IsTrue(ex is SpecificExceptionType);
}

3
이것은 작동하지만 논리가 지나치게 복잡하기 때문에 일반적으로 권장하지 않습니다. 복잡한 말은 아니지만 여러 테스트 (10, 100, 테스트)에 대해이 코드 블록을 작성하는지 고려하십시오. 이 논리는 잘 설계된 어설 션 방법으로 구성해야합니다. 다른 답변을 참조하십시오.
steve

35

다음과 같이 ExpectedException 사용에주의하십시오.

http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx

그리고 여기:

http://xunit.github.io/docs/comparisons.html

예외를 테스트해야하는 경우 방법에 대한 찌푸림이 적습니다. try {act / fail} catch {assert} 메소드를 사용하면 ExpectedException 이외의 예외 테스트를 직접 지원하지 않는 프레임 워크에 유용 할 수 있습니다.

더 나은 대안은 xUnit.NET을 사용하는 것입니다. xUnit.NET은 다른 모든 실수에서 배워 개선 된 매우 현대적이고 미래 지향적이며 확장 가능한 단위 테스트 프레임 워크입니다. 이러한 개선 사항 중 하나는 Assert.Throws이며 예외를 주장하는 데 훨씬 더 좋은 구문을 제공합니다.

github에서 xUnit.NET을 찾을 수 있습니다 : http://xunit.github.io/


4
NUnit 2.5는 이제 Assert.Throws 스타일 구문도 지원합니다. nunit.com/index.php?p=releaseNotes&r=2.5
Alconja

ExpectedException을 사용할 때 예외에 대해 알리기 위해 단위 테스트가 중지되는 방식은 나를 미치게합니다. MS가 자동 테스트에서 수동 단계를 수행하는 것이 좋은 생각이라고 생각한 이유는 무엇입니까? 링크 주셔서 감사합니다.
개미

@Ant : MS는 NUnit을 복사했습니다 ... 그래서 실제 질문은 NUnit이 왜 그것이 좋은 생각이라고 생각 했습니까?
jrista

28

MSTest (v2)에는 이제 다음과 같이 사용할 수있는 Assert.ThrowsException 함수가 있습니다.

Assert.ThrowsException<System.FormatException>(() =>
            {
                Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
            }); 

너겟으로 설치할 수 있습니다 : Install-Package MSTest.TestFramework


2018 년에는 테스트중인 장치 만 던지고 다른 코드는 검사하지 않기 때문에 모범 사례로 간주됩니다.
CM

24

내가 작업중 인 프로젝트에서 우리는 이것을하는 또 다른 솔루션을 가지고 있습니다.

먼저 ExpectedExceptionAttribute가 마음에 들지 않아 예외를 일으킨 메소드 호출을 고려합니다.

대신 도우미 메서드 로이 작업을 수행합니다.

테스트

[TestMethod]
public void AccountRepository_ThrowsExceptionIfFileisCorrupt()
{
     var file = File.Create("Accounts.bin");
     file.WriteByte(1);
     file.Close();

     IAccountRepository repo = new FileAccountRepository();
     TestHelpers.AssertThrows<SerializationException>(()=>repo.GetAll());            
}

헬퍼 메소드

public static TException AssertThrows<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (TException ex)
        {
            return ex;
        }
        Assert.Fail("Expected exception was not thrown");

        return null;
    }

깔끔하지 않습니까?)


14

테스트 방법의 속성입니다. Assert를 사용하지 않습니다. 다음과 같습니다 :

[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()

13

nget / xUnit 스타일의 Assert.Throws () 구문을 MsTest에 추가하는 PM> Install-Package MSTestExtensions 를 사용하여 Nuget에서 패키지를 다운로드 할 수 있습니다 .

고급 지침 : 어셈블리를 다운로드하고 BaseTest 에서 상속 하면 Assert.Throws () 구문을 사용할 수 있습니다 .

Throws 구현의 주요 메소드는 다음과 같습니다.

public static void Throws<T>(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception
{
    try
    {
        task();
    }
    catch (Exception ex)
    {
        AssertExceptionType<T>(ex);
        AssertExceptionMessage(ex, expectedMessage, options);
        return;
    }

    if (typeof(T).Equals(new Exception().GetType()))
    {
        Assert.Fail("Expected exception but no exception was thrown.");
    }
    else
    {
        Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.", typeof(T)));
    }
}

공개 :이 패키지를 정리했습니다.

추가 정보 : http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html


예를 주셔서 감사합니다. Assert.DoesNotThrow () 또는 이와 동등한 것을 테스트하는 방법에 대한 예가 있습니까?
Lane Goolsby 2016 년

10

간단한 한 줄로이를 달성 할 수 있습니다.

작업 foo.bar()이 비동기 인 경우 :

await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());

foo.bar()비동기가 아닌 경우

Assert.ThrowsException<Exception>(() => foo.bar());

1
다른 답변이 많이 있습니다. 저는 예외 유형으로 알려진 실패 조건을 테스트하는 간단한 방법을 찾고 있었으므로 가장 읽기 쉬운 테스트 사례가되었습니다. 참고 : 예외 유형은 표준 try-catch와 같은 상속 된 예외 클래스에서 일치하지 않으므로 위의 예는 예를 들어 트랩하지 않습니다 ArgumentException. 테스트 할 고급 기준이있는 경우 이전 Try Catch 및 예외 응답 테스트는 여전히 선호되지만, 많은 경우에 이것은 많은 도움이됩니다!
Chris Schaller

5

ExpectedException 속성을 사용하는 것은 (너무 제한적이고 오류가 발생하기 쉽기 때문에) 또는 각 테스트에서 try / catch 블록을 작성하는 것은 권장하지 않습니다 (너무 복잡하고 오류가 발생하기 쉽기 때문에). 테스트 프레임 워크에서 제공하거나 직접 작성하는 잘 설계된 어설 션 방법을 사용하십시오. 여기 내가 쓰고 사용하는 것이 있습니다.

public static class ExceptionAssert
{
    private static T GetException<T>(Action action, string message="") where T : Exception
    {
        try
        {
            action();
        }
        catch (T exception)
        {
            return exception;
        }
        throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated.  " + message);
    }

    public static void Propagates<T>(Action action) where T : Exception
    {
        Propagates<T>(action, "");
    }

    public static void Propagates<T>(Action action, string message) where T : Exception
    {
        GetException<T>(action, message);
    }

    public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
    {
        Propagates(action, validation, "");
    }

    public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
    {
        validation(GetException<T>(action, message));
    }
}

사용 예 :

    [TestMethod]
    public void Run_PropagatesWin32Exception_ForInvalidExeFile()
    {
        (test setup that might propagate Win32Exception)
        ExceptionAssert.Propagates<Win32Exception>(
            () => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
        (more asserts or something)
    }

    [TestMethod]
    public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
    {
        (test setup that might propagate FileNotFoundException)
        ExceptionAssert.Propagates<FileNotFoundException>(
            () => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
            e => StringAssert.Contains(e.Message, "NotThere.exe"));
        (more asserts or something)
    }

노트

유효성 검사 콜백을 지원하는 대신 예외를 반환하면이 어설트의 호출 구문이 내가 사용하는 다른 어설트와 매우 다르다는 점을 제외하고는 합리적인 아이디어입니다.

다른 것과 달리, 'throws'대신 'propagates'를 사용합니다. 예외가 호출에서 전파되는지 여부 만 테스트 할 수 있기 때문입니다. 예외가 발생했는지 직접 테스트 할 수 없습니다. 그러나 이미지 던지기를 의미한다고 생각합니다.

최종 생각

이러한 종류의 접근 방식으로 전환하기 전에 테스트에서 예외 유형 만 확인했을 때 ExpectedException 특성을 사용하고 추가 유효성 검사가 필요한 경우 try / catch 블록을 사용하는 것을 고려했습니다. 그러나 각 테스트에 사용할 기술에 대해 생각해야 할뿐만 아니라 필요에 따라 코드를 한 기술에서 다른 기술로 변경하는 것이 쉬운 일이 아닙니다. 하나의 일관된 접근 방식을 사용하면 정신적 인 노력이 절약됩니다.

요약하면,이 접근 방식은 사용 편의성, 유연성 및 견고성 (잘못하기 어렵다)입니다.


4

위의 @Richiban에서 제공하는 도우미는 예외가 발생하는 상황을 처리하지 않지만 예상되는 유형은 처리하지 않는 한 훌륭하게 작동합니다. 다음은이를 해결합니다.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        /// <summary>
        /// Helper for Asserting that a function throws an exception of a particular type.
        /// </summary>
        public static void Throws<T>( Action func ) where T : Exception
        {
            Exception exceptionOther = null;
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }
            catch (Exception e) {
                exceptionOther = e;
            }

            if ( !exceptionThrown )
            {
                if (exceptionOther != null) {
                    throw new AssertFailedException(
                        String.Format("An exception of type {0} was expected, but not thrown. Instead, an exception of type {1} was thrown.", typeof(T), exceptionOther.GetType()),
                        exceptionOther
                        );
                }

                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but no exception was thrown.", typeof(T))
                    );
            }
        }
    }
}

2
흠 ... 나는 그 아이디어를 이해하지만 그것이 더 낫다는 것에 확신이 서지 않습니다. 특정 예외가 발생했는지 확인한다고해서 다른 모든 예외가 어설 션 오류로 마무리되어야하는 것은 아닙니다. 알 수없는 예외는 다른 어설 션 작업에서와 마찬가지로 스택을 버블 링해야합니다.
Crono

@Martin 나는 exceptionOther와 관련된 코드를 제거하고 간단히 두 번째 catch 절
Tom Lint

4

다른 테스트 클래스 사용을 언급 했으므로ExpectedException 속성 보다 더 나은 옵션 은 Shoudly 's should.Throw 를 사용하는 입니다.

Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });

고객주문 을 작성하려면 주소 가 있어야 한다는 요구 사항이 있다고 가정 해 봅시다 . 그렇지 않은 경우 메소드는을 생성해야합니다 . 그런 다음 우리는 다음과 같이 쓸 수 있습니다.CreateOrderForCustomerArgumentException

[TestMethod]
public void NullUserIdInConstructor()
{
  var customer = new Customer(name := "Justin", address := null};

  Should.Throw<ArgumentException>(() => {
    var order = CreateOrderForCustomer(customer) });
}

이것은 사용하는 것보다 낫다 ExpectedException오류를 발생시킬 대상을 구체적으로 지정하기 때문에 속성을 . 이를 통해 테스트 요구 사항이보다 명확 해지고 테스트 실패시 진단이 쉬워집니다.

또한이 주 Should.ThrowAsync비동기 방식의 테스트를 위해.


4

대안으로 테스트를 시도 할 수 있습니다. 실제로 테스트에서 다음 두 줄로 예외가 발생합니다.

var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);

4

VS 내장 단위 테스트에서 단순히 "예외"가 발생했는지 확인하고 싶지만 유형을 모르는 경우 모두 catch를 사용할 수 있습니다.

[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void ThrowExceptionTest()
{
    //...
}

3

글쎄, 나는 여기에 다른 사람들이 전에 말한 것을 요약 할 것입니다 ... 어쨌든, 여기에 좋은 답변에 따라 빌드 한 코드가 있습니다 :) 모든 것은 복사하고 사용하는 것입니다 ...

/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
    try
    {
        methodToExecute();
    }
    catch (TException) {
        return;
    }  
    catch (System.Exception ex)
    {
        Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
    }
    Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");  
}


2

이것은 어떤 테스트 프레임 워크를 사용하고 있습니까?

예를 들어 MbUnit에서 예상 예외를 속성으로 지정하면 실제로 예상되는 예외를 얻을 수 있습니다.

[ExpectedException(typeof(ArgumentException))]

2

NUnit 을 사용하는 경우 다음을 시도하십시오.

Assert.That(() =>
        {
            Your_Method_To_Test();
        }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));

2

NFluent 라는 멋진 라이브러리가있어 어설 션 작성 속도빨라집니다 .

예외를 던지기위한 주장을 작성하는 것은 매우 간단합니다.

    [Test]
    public void given_when_then()
    {
        Check.ThatCode(() => MethodToTest())
            .Throws<Exception>()
            .WithMessage("Process has been failed");
    }

1

이것은 오래된 질문이지만 토론에 새로운 생각을 추가하고 싶습니다. Arrange, Act, Assert 패턴을 예상, Arrange, Act, Assert로 확장했습니다. 예상되는 예외 포인터를 만든 다음 지정된 예외 포인터를 지정할 수 있습니다. 이것은 catch 블록에서 Asserts를 수행하는 것보다 깨끗하게 느껴지므로 Act 섹션은 대부분 한 줄의 코드에 대해서만 테스트중인 메소드를 호출합니다. 당신은 또한에없는 Assert.Fail();또는 return코드의 여러 지점에서. 다른 예외가 발생하면 테스트가 포착되지 않기 때문에 테스트가 실패하고 예상되는 유형의 예외가 발생하지만 예상 한 유형이 아닌 경우 메시지 또는 기타 속성에 대한 어설 션 예외는 테스트가 실수로 통과하지 않도록하는 데 도움이됩니다.

[TestMethod]
public void Bar_InvalidDependency_ThrowsInvalidOperationException()
{
    // Expectations
    InvalidOperationException expectedException = null;
    string expectedExceptionMessage = "Bar did something invalid.";

    // Arrange
    IDependency dependency = DependencyMocks.Create();
    Foo foo = new Foo(dependency);

    // Act
    try
    {
        foo.Bar();
    }
    catch (InvalidOperationException ex)
    {
        expectedException = ex;
    }

    // Assert
    Assert.IsNotNull(expectedException);
    Assert.AreEqual(expectedExceptionMessage, expectedException.Message);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.