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 ()을 다시 발생시켜야합니다. 그러나 실제로, 이것은 당신이 이것을 손으로 쓰면 안된다는 신호입니다. 테스트 프레임 워크에서 옵션을 확인하거나 테스트를 위해 더 의미있는 예외를 던질 수 있는지 확인하십시오.
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());
}
나머지 단위 테스트 구문과 훨씬 비슷하게 보이고 동작합니다.
Assert.ThrowsException<T>
와 Assert.ThrowsExceptionAsync<T>
- 볼 blogs.msdn.microsoft.com/visualstudioalm/2017/02/25/...
NUNIT을 사용하면 다음과 같이 할 수 있습니다.
Assert.Throws<ExpectedException>(() => methodToTest());
추가로 확인하기 위해 발생 된 예외를 저장할 수도 있습니다.
ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);
원래 ExpectedException
속성 이 없었던 MSTest를 사용하는 경우 다음을 수행 할 수 있습니다.
try
{
SomeExceptionThrowingMethod()
Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
Assert.IsTrue(ex is SpecificExceptionType);
}
다음과 같이 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/
MSTest (v2)에는 이제 다음과 같이 사용할 수있는 Assert.ThrowsException 함수가 있습니다.
Assert.ThrowsException<System.FormatException>(() =>
{
Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
});
너겟으로 설치할 수 있습니다 : Install-Package MSTest.TestFramework
내가 작업중 인 프로젝트에서 우리는 이것을하는 또 다른 솔루션을 가지고 있습니다.
먼저 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;
}
깔끔하지 않습니까?)
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
간단한 한 줄로이를 달성 할 수 있습니다.
작업 foo.bar()
이 비동기 인 경우 :
await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());
foo.bar()
비동기가 아닌 경우
Assert.ThrowsException<Exception>(() => foo.bar());
ArgumentException
. 테스트 할 고급 기준이있는 경우 이전 Try Catch 및 예외 응답 테스트는 여전히 선호되지만, 많은 경우에 이것은 많은 도움이됩니다!
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 블록을 사용하는 것을 고려했습니다. 그러나 각 테스트에 사용할 기술에 대해 생각해야 할뿐만 아니라 필요에 따라 코드를 한 기술에서 다른 기술로 변경하는 것이 쉬운 일이 아닙니다. 하나의 일관된 접근 방식을 사용하면 정신적 인 노력이 절약됩니다.
요약하면,이 접근 방식은 사용 편의성, 유연성 및 견고성 (잘못하기 어렵다)입니다.
위의 @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))
);
}
}
}
}
다른 테스트 클래스 사용을 언급 했으므로ExpectedException
속성 보다 더 나은 옵션 은 Shoudly 's should.Throw 를 사용하는 것 입니다.
Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });
고객 이 주문 을 작성하려면 주소 가 있어야 한다는 요구 사항이 있다고 가정 해 봅시다 . 그렇지 않은 경우 메소드는을 생성해야합니다 . 그런 다음 우리는 다음과 같이 쓸 수 있습니다.CreateOrderForCustomer
ArgumentException
[TestMethod]
public void NullUserIdInConstructor()
{
var customer = new Customer(name := "Justin", address := null};
Should.Throw<ArgumentException>(() => {
var order = CreateOrderForCustomer(customer) });
}
이것은 사용하는 것보다 낫다 ExpectedException
오류를 발생시킬 대상을 구체적으로 지정하기 때문에 속성을 . 이를 통해 테스트 요구 사항이보다 명확 해지고 테스트 실패시 진단이 쉬워집니다.
또한이 주 Should.ThrowAsync
비동기 방식의 테스트를 위해.
글쎄, 나는 여기에 다른 사람들이 전에 말한 것을 요약 할 것입니다 ... 어쨌든, 여기에 좋은 답변에 따라 빌드 한 코드가 있습니다 :) 모든 것은 복사하고 사용하는 것입니다 ...
/// <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.");
}
이것은 오래된 질문이지만 토론에 새로운 생각을 추가하고 싶습니다. 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);
}