programing

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

nasanasas 2020. 9. 29. 07:54
반응형

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


Assert (또는 다른 테스트 클래스)를 사용하여 예외가 발생했는지 확인하는 방법은 무엇입니까?


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

일반적으로 테스트 프레임 워크에 이에 대한 답이 있습니다. 그러나 충분히 유연하지 않은 경우 언제든지 다음을 수행 할 수 있습니다.

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 ()을 다시 던져야합니다. 하지만 실제로 이것은 손으로 쓰지 말아야한다는 신호입니다. 테스트 프레임 워크에서 옵션을 확인하거나 테스트 할 더 의미있는 예외를 throw 할 수 있는지 확인하세요.

catch (AssertionException) { throw; }

포착 할 예외의 종류를 지정하는 등 원하는대로이 접근 방식을 적용 할 수 있어야합니다. 특정 유형 만 예상하는 경우 다음으로 catch블록을 완료하십시오 .

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

이를 구현하는 데 선호하는 방법은 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());
}

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


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

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

NUNIT를 사용하는 경우 다음과 같이 할 수 있습니다.

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


추가로 유효성을 검사하기 위해 throw 된 예외를 저장할 수도 있습니다.

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


ExpectedException을 사용하면 다음과 같이 여러 가지 함정이 발생할 수 있으므로주의하십시오.

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

그리고 여기:

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

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

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

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


작업중인 프로젝트에서이 작업을 수행하는 또 다른 솔루션이 있습니다.

먼저 예외를 발생시킨 메서드 호출을 고려하기 때문에 ExpectedExceptionAttribute가 마음에 들지 않습니다.

대신 helpermethod를 사용합니다.

테스트

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

HelperMethod

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

깔끔하지 않나요?)


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

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

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


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

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

NUnit / 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


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

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

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

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

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

ExpectedException 속성을 사용하거나 (너무 제한적이고 오류가 발생하기 쉬우므로) 각 테스트에서 try / catch 블록을 작성하지 않는 것이 좋습니다 (너무 복잡하고 오류가 발생하기 쉬우므로). 테스트 프레임 워크에서 제공하거나 직접 작성하여 잘 설계된 assert 메서드를 사용하십시오. 여기에 내가 쓰고 사용한 것이 있습니다.

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 블록을 사용하는 것을 고려했습니다. 그러나 각 테스트에 어떤 기술을 사용할지 생각해야 할뿐만 아니라 필요에 따라 코드를 한 기술에서 다른 기술로 변경하는 것은 쉬운 일이 아닙니다. 일관된 접근 방식을 사용하면 정신적 노력이 절약됩니다.

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


위의 @Richiban에서 제공하는 도우미는 예외가 throw되는 상황을 처리하지 않지만 예상되는 유형을 처리하지 않는다는 점을 제외하면 훌륭하게 작동합니다. 다음 주소 :

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

Since you mention using other test classes, a better option than the ExpectedException attribute is to use Shoudly's Should.Throw.

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

Let's say we have a requirement that the customer must have an address to create an order. If not, the CreateOrderForCustomer method should result in an ArgumentException. Then we could write:

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

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

This is better than using an ExpectedException attribute because we are being specific about what should throw the error. This makes requirements in our tests clearer and also makes diagnosis easier when the test fails.

Note there is also a Should.ThrowAsync for asynchronous method testing.


As an alternative you can try testing exceptions are in fact being thrown with the next 2 lines in your test.

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

Well i'll pretty much sum up what everyone else here said before...Anyways, here's the code i built according to the good answers :) All is left to do is copy and use...

/// <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.");  
}

In VS built-in unit testing if you simply want to verify that "any exception" is thrown, but you don't know the type, you can use a catch all:

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

Check out nUnit Docs for examples about:

[ExpectedException( typeof( ArgumentException ) )]

This is going to depend on what test framework are you using?

In MbUnit, for example, you can specify the expected exception with an attribute to ensure that you are getting the exception you really expect.

[ExpectedException(typeof(ArgumentException))]

In case of using NUnit, try this:

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

Even though this is an old question, I would like to add a new thought to the discussion. I have extended the Arrange, Act, Assert pattern to be Expected, Arrange, Act, Assert. You can make an expected exception pointer, then assert it was assigned to. This feels cleaner than doing your Asserts in a catch block, leaving your Act section mostly just for the one line of code to call the method under test. You also don't have to Assert.Fail(); or return from multiple points in the code. Any other exception thrown will cause the test to fail, because it won't be caught, and if an exception of your expected type is thrown, but the it wasn't the one you were expecting, Asserting against the message or other properties of the exception help make sure your test won't pass inadvertently.

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

There is an awesome library called NFluent which speeds up and eases the way you write your assertions.

It is pretty straightforward to write an assertion for throwing an exception:

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

참고URL : https://stackoverflow.com/questions/933613/how-do-i-use-assert-to-verify-that-an-exception-has-been-thrown

반응형