Assert.Throws를 사용하여 예외 유형을 확인하려면 어떻게해야합니까?


247

Assert.Throws예외 유형과 실제 메시지 문구를 확인 하는 데 어떻게 사용합니까?

이 같은:

Assert.Throws<Exception>(
    ()=>user.MakeUserActive()).WithMessage("Actual exception message")

테스트하고있는 방법은 다른 메시지와 함께 동일한 유형의 여러 메시지를 throw하므로 컨텍스트에 따라 올바른 메시지가 발생하는지 테스트하는 방법이 필요합니다.

답변:


444

Assert.Throws throw 된 예외를 반환하여 예외를 주장 할 수 있습니다.

var ex = Assert.Throws<Exception>(() => user.MakeUserActive());
Assert.That(ex.Message, Is.EqualTo("Actual exception message"));

따라서 예외가 발생하지 않거나 잘못된 유형의 예외가 발생하면 첫 번째 Assert.Throws어설 션이 실패합니다. 그러나 올바른 유형의 예외가 발생하면 변수에 저장 한 실제 예외를 주장 할 수 있습니다.

이 패턴을 사용하면 예외 메시지 이외의 다른 것을 주장 할 수 있습니다 (예 : ArgumentException및 파생 상품 의 경우) . 매개 변수 이름이 올바르다 고 주장 할 수 있습니다.

var ex = Assert.Throws<ArgumentNullException>(() => foo.Bar(null));
Assert.That(ex.ParamName, Is.EqualTo("bar"));

유창한 API를 사용하여 이러한 어설 션을 수행 할 수도 있습니다.

Assert.That(() => foo.Bar(null), 
Throws.Exception
  .TypeOf<ArgumentNullException>()
  .With.Property("ParamName")
  .EqualTo("bar"));

또는 대안 적으로

Assert.That(
    Assert.Throws<ArgumentNullException>(() =>
        foo.Bar(null)
    .ParamName,
Is.EqualTo("bar"));

예외 메시지를 주장 할 때 약간의 팁 SetCultureAttribute은 던져진 메시지가 예상 문화권을 사용하고 있는지 확인하기 위해 테스트 방법을 장식하는 것입니다. 예외 메시지를 지역화 할 수있는 리소스로 저장하면 작동합니다.


이것은 정말 도움이되었습니다. 오류를 표시하는 방법을 원했습니다 .Assert.Throws 메서드에서 값을 반환하면 읽지조차 못했습니다. 감사합니다
Haroon

6
+1 Fluent API를 보여 주셔서 감사합니다. 어떤 이유로 NUnit 문서에서만 사용하는 방법을 이해하는 데 문제가있었습니다.
aolszowka

메시지를 주장하려는 경우 "속성"대신 메시지 속성을 직접 사용할 수도 있습니다.
Marcel

25

이제 ExpectedException속성을 사용할 수 있습니다 . 예 :

[Test]
[ExpectedException(typeof(InvalidOperationException), 
 ExpectedMessage="You can't do that!"]
public void MethodA_WithNull_ThrowsInvalidOperationException()
{
    MethodA(null);
}

2
테스트 겉보기에는 어떤 주장도 없었기 때문에 이것은 처음에 볼 때 조금 혼란 스러웠습니다. 이것은 좋은 기능이지만, 일이 attribut가 Assert.Throws을 통해 익숙해해야 처리 여부 팀에서 논의해야
마르셀

14
+1은 예외를 테스트하는 좋은 방법입니다. 이것에 대해 명심해야 할 유일한 것은 이론적으로 해당 메시지와 함께 InvalidOperationException을 던지는 모든 코드 줄은 테스트 데이터 / 객체를 준비하는 테스트 코드를 포함하여 테스트를 통과한다는 것입니다. 테스트에 관심이있는 사람은 오 탐지를 일으킬 수 있습니다. 물론, 메시지의 특정 정도와 테스트중인 예외 유형에 따라 다릅니다. Assert.Throw당신이 원하는 정확한 라인을 대상 으로 할 수 있습니다.
Nope

21
ExpectedException 속성은 NUnit 3에서 더 이상 사용되지 않습니다 : github.com/nunit/docs/wiki/Breaking-Changes
Frank Sebastià

13
Assert.That(myTestDelegate, Throws.ArgumentException
    .With.Property("Message").EqualTo("your argument is invalid."));

2
이름 연산자의 도입으로 나는이 훌륭한 답변을 다음과 같이 편집 할 것입니다 :Assert.That(myTestDelegate, Throws.ArgumentException .With.Property(nameof(ArgumentException.Message)).EqualTo("your argument is invalid."));
Samuel

@Samuel이 편집은 강력하게 형식화 된 참조를 사용하지만, 반면에 속성 이름이 매우 낮고 마법 문자열이 유창성을 향상시킵니다. 내가 생각하는 맛의 문제
Jordan Morris

1
나는 당신에 대해 전적으로 동의합니다 Exception.Message. With.Property다른 객체에서도 활용할 수 있기 때문에 적어도이 대안을 추가하는 것이 좋습니다. 이 경우 코드의 안정성이 향상됩니다.
사무엘

5

이것은 오래된 답변과 함께 오래되었지만 관련성이 높은 질문이므로 현재 솔루션을 추가하고 있습니다.

public void Test() {
    throw new MyCustomException("You can't do that!");
}

[TestMethod]
public void ThisWillPassIfExceptionThrown()
{
    var exception = Assert.ThrowsException<MyCustomException>(
        () => Test(),
        "This should have thrown!");
    Assert.AreEqual("You can't do that!", exception.Message);
}

이것은 함께 작동 using Microsoft.VisualStudio.TestTools.UnitTesting;


JUnit에서와 같이 메소드가 예외를 throw한다고 주장하는 간결한 방법이 없다는 것이 정말 놀랐습니다. 내가 알지 못하는 의미가 없다면, 이것은 아마도 가장 관련성이 높은 대답 일 것입니다.
NetherGranite

3

퍼시 스턴트의 답변을 넓히고 NUnit의 더 많은 기능을 제공하기 위해 다음과 같이 할 수 있습니다.

public bool AssertThrows<TException>(
    Action action,
    Func<TException, bool> exceptionCondition = null)
    where TException : Exception 
{
    try
    {
        action();
    }
    catch (TException ex)
    {
        if (exceptionCondition != null)
        {
            return exceptionCondition(ex);
        }

        return true;
    }
    catch
    {
        return false;
    }

    return false; 
}

예 :

// No exception thrown - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => {}));

// Wrong exception thrown - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new ApplicationException(); }));

// Correct exception thrown - test passes.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException(); }));

// Correct exception thrown, but wrong message - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException("ABCD"); },
        ex => ex.Message == "1234"));

// Correct exception thrown, with correct message - test passes.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException("1234"); },
        ex => ex.Message == "1234"));

2

이 문제가 제기 된 지 오랜 시간이 걸렸지 만 최근에 같은 문제가 발생하여 MSTest에 대해이 기능을 제안합니다.

public bool AssertThrows(Action action) where T : Exception 
{ 
try {action();} 
catch(Exception exception) 
{ 
    if (exception.GetType() == typeof(T)) return true; 
} 
return false; 
}

용법:

Assert.IsTrue(AssertThrows<FormatException>(delegate{ newMyMethod(MyParameter); }));

더 여기 : http://phejndorf.wordpress.com/2011/02/21/assert-that-a-particular-exception-has-occured/


2

새로운 NUnit 패턴의 일부가 어색하기 때문에 다음과 같은 것을 사용하여 개인적으로 더 깨끗한 코드를 만듭니다.

public void AssertBusinessRuleException(TestDelegate code, string expectedMessage)
{
    var ex = Assert.Throws<BusinessRuleException>(code);
    Assert.AreEqual(ex.Message, expectedMessage);
}

public void AssertException<T>(TestDelegate code, string expectedMessage) where T:Exception
{
    var ex = Assert.Throws<T>(code);
    Assert.AreEqual(ex.Message, expectedMessage);
}

사용법은 다음과 같습니다.

AssertBusinessRuleException(() => user.MakeUserActive(), "Actual exception message");

1
TestDelegate 란 무엇입니까?
reggaeguitar

1
매개 변수로 실행할 코드를 전달할 수 있습니다. NUnit 프레임 워크 (v3.2.0.0)의 클래스입니다.
야만인
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.