Assert
Bir istisnanın atıldığını doğrulamak için (veya başka bir Test sınıfını) nasıl kullanabilirim ?
Assert
Bir istisnanın atıldığını doğrulamak için (veya başka bir Test sınıfını) nasıl kullanabilirim ?
Yanıtlar:
"Visual Studio Team Test" için test yöntemine ExpectedException özniteliğini uyguladığınız görünür.
Buradaki belgelerden örnek: Visual Studio Team Testiyle Birim Testi İzlenecek Yol
[TestMethod]
[ExpectedException(typeof(ArgumentException),
"A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}
Genellikle test çerçeveniz bunun için bir cevap verecektir. Ancak yeterince esnek değilse, bunu her zaman yapabilirsiniz:
try {
somethingThatShouldThrowAnException();
Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }
@Jonas'ın belirttiği gibi, bu bir temel İstisna yakalamak için işe yaramaz:
try {
somethingThatShouldThrowAnException();
Assert.Fail(); // raises AssertionException
} catch (Exception) {
// Catches the assertion exception, and the test passes
}
Kesinlikle İstisna yakalamanız gerekiyorsa, Assert.Fail () yöntemini yeniden oluşturmanız gerekir. Ama gerçekten, bu bunu el yazmamanız gereken bir işaret; seçenekler için test çerçevenizi kontrol edin veya test etmek için daha anlamlı bir istisna yapıp yapamayacağınıza bakın.
catch (AssertionException) { throw; }
Bu yaklaşımı, ne tür istisnaları yakalamak dahil olmak üzere, istediğiniz her şeye uyarlayabilmelisiniz. Yalnızca belirli türler bekliyorsanız, catch
blokları aşağıdakilerle bitirin :
} catch (GoodException) {
} catch (Exception) {
// not the right kind of exception
Assert.Fail();
}
Bunu uygulamak için tercih ettiğim yöntem Throws adlı bir yöntem yazmak ve bunu diğer Assert yöntemlerinde olduğu gibi kullanmaktır. Ne yazık ki, .NET statik bir uzantı yöntemi yazmanıza izin vermez, bu nedenle bu yöntemi aslında Assert sınıfındaki bir yapıya aitmiş gibi kullanamazsınız; MyAssert veya benzeri bir şey yapın. Sınıf şöyle görünür:
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))
);
}
}
}
}
Bu, birim testinizin şöyle görüneceği anlamına gelir:
[TestMethod()]
public void ExceptionTest()
{
String testStr = null;
MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}
Hangi birim test sözdizimlerinizin geri kalanı gibi görünüyor ve davranıyor.
Assert.ThrowsException<T>
ve Assert.ThrowsExceptionAsync<T>
bkz. Blogs.msdn.microsoft.com/visualstudioalm/2017/02/25/…
NUNIT kullanıyorsanız, böyle bir şey yapabilirsiniz:
Assert.Throws<ExpectedException>(() => methodToTest());
Daha ileri doğrulamak için atılan istisnayı da saklamak mümkündür:
ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);
Başlangıçta bir ExpectedException
niteliği olmayan MSTest kullanıyorsanız , bunu yapabilirsiniz:
try
{
SomeExceptionThrowingMethod()
Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
Assert.IsTrue(ex is SpecificExceptionType);
}
Burada gösterildiği gibi birkaç tuzağa neden olabileceğinden, ExpectedException'ı kullanmaya dikkat edin:
http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx
Ve burada:
http://xunit.github.io/docs/comparisons.html
İstisnaları test etmeniz gerekiyorsa, yollarda daha az kaşlarını çattı. ExpectedException dışındaki özel durum testleri için doğrudan desteği olmayan çerçeveler için yararlı olabilecek try {act / fail} catch {assert} yöntemini kullanabilirsiniz.
Daha iyi bir alternatif, diğer tüm hatalardan öğrenilen ve geliştirilmiş çok modern, ileriye dönük ve genişletilebilir bir birim test çerçevesi olan xUnit.NET'i kullanmaktır. Böyle bir gelişme, istisnaları öne sürmek için çok daha iyi bir sözdizimi sağlayan Assert.Throws'dur.
XUnit.NET'i github'da bulabilirsiniz: http://xunit.github.io/
MSTest (v2) artık şu şekilde kullanılabilen Assert.ThrowsException işlevine sahiptir:
Assert.ThrowsException<System.FormatException>(() =>
{
Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
});
Nuget ile kurabilirsiniz: Install-Package MSTest.TestFramework
Üzerinde çalıştığım bir projede bunu yapan başka bir çözümümüz var.
İlk olarak ExpectedExceptionAttribute sevmiyorum hangi istisna neden hangi yöntem çağrısı dikkate alır çünkü sevmiyorum.
Bunu bir yardımcı yöntemle yapıyorum.
Ölçek
[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;
}
Düzgün, değil;)
Test yönteminin bir özelliğidir ... Assert kullanmazsınız. Buna benzer:
[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()
Nuget'ten şu paketi kullanarak bir paket indirebilirsiniz: PM> Install-Package MSTestExtensions , MsTest'e nUnit / xUnit tarzında Assert.Throws () sözdizimi ekler .
Üst düzey talimatlar: montajı indirin ve BaseTest'ten devralın , Assert.Throws () sözdizimini kullanabilirsiniz.
Throws uygulamasının ana yöntemi aşağıdaki gibidir:
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)));
}
}
Açıklama: Bu paketi bir araya getirdim.
Daha Fazla Bilgi: http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html
Bunu basit bir tek satırla yapabilirsiniz.
Operasyonunuz Eğer foo.bar()
zaman uyumsuz geçerli:
await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());
Eğer foo.bar()
zaman uyumsuz değildir
Assert.ThrowsException<Exception>(() => foo.bar());
ArgumentException
. Eski Try Catch ve test istisna yanıtı, test etmek için gelişmiş kriterleriniz varsa hala tercih edilir, ancak çoğu durumum için bu çok yardımcı olur!
ExpectedException özniteliğini (çok kısıtlayıcı ve hata eğilimli olduğundan) veya her sınamada bir try / catch bloğu yazmak için (çok karmaşık ve hataya yatkın olduğundan) önerilmez. Test çerçeveniz tarafından sağlanan veya kendiniz yazdığınız iyi tasarlanmış bir onaylama yöntemi kullanın. İşte yazdıklarım ve kullandıklarım.
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));
}
}
Örnek kullanımlar:
[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)
}
NOTLAR
Doğrulama geri aramasını desteklemek yerine istisnayı döndürmek makul bir fikirdir, ancak bunu yapmak bu iddianın çağrı sözdizimini kullandığım diğer iddialardan çok farklı kılar.
Diğerlerinden farklı olarak, sadece bir çağrıdan bir istisnanın yayılıp yayılmadığını test edebileceğimiz için, 'atar' yerine 'yayılımları' kullanıyorum. Bir istisna atıldığını doğrudan test edemeyiz. Ama sanırım görüntü atışları demek olabilir: atıldı ve yakalanmadı.
SON DÜŞÜNCE
Bu tür bir yaklaşıma geçmeden önce, bir test yalnızca istisna türünü doğruladığında ExpectedException özniteliğini ve daha fazla doğrulama gerekiyorsa bir try / catch bloğu kullanmayı düşündüm. Ancak, sadece her test için hangi tekniğin kullanılacağını düşünmek zorunda kalmayacağım, aynı zamanda ihtiyaçlar değiştikçe kodu bir teknikten diğerine değiştirmek önemsiz bir çaba değildi. Tek bir tutarlı yaklaşım kullanmak zihinsel çabadan tasarruf sağlar.
Özetle, bu yaklaşım spor: kullanım kolaylığı, esneklik ve sağlamlık (yanlış yapmak zor).
Yukarıdaki @Richiban tarafından sağlanan yardımcı, bir istisna atıldığı durumla başa çıkmadığı, ancak beklenen türle ilgilenmediği için harika çalışıyor. Aşağıdaki adresler:
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))
);
}
}
}
}
Diğer test sınıflarını kullandığınızdan bahsettiğiniz için, ExpectedException
nitelikten daha iyi bir seçenek Shoudly 's Should.Throw'u kullanmaktır .
Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });
Diyelim ki müşteri , sipariş oluşturmak için bir adrese sahip olmalı . Değilse, yöntem bir . Sonra şunu yazabiliriz:CreateOrderForCustomer
ArgumentException
[TestMethod]
public void NullUserIdInConstructor()
{
var customer = new Customer(name := "Justin", address := null};
Should.Throw<ArgumentException>(() => {
var order = CreateOrderForCustomer(customer) });
}
Bu, bir ExpectedException
özniteliği kullanmaktan daha iyidir çünkü hatayı neyin atması gerektiği konusunda spesifik davranıyoruz. Bu, testlerimizdeki gereksinimleri daha net hale getirir ve test başarısız olduğunda teşhisi kolaylaştırır.
Ayrıca Should.ThrowAsync
, eşzamansız bir yöntem testi için de olduğunu unutmayın .
Peki ben hemen hemen herkes burada ne dedi özetlemek ... Her neyse, işte ben iyi cevaplara göre inşa kod :) Yapmanız gereken tek şey kopyalamak ve kullanmak ...
/// <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.");
}
Check out nUnit Dokümanlar hakkında örnekler için:
[ExpectedException( typeof( ArgumentException ) )]
NUnit kullanılması durumunda şunu deneyin:
Assert.That(() =>
{
Your_Method_To_Test();
}, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));
Denilen müthiş bir kütüphane bulunmaktadır NFluent size iddialarını yazma biçimini hızlandırır ve kolaylaştırır .
Bir istisna atmak için bir iddia yazmak oldukça basittir:
[Test]
public void given_when_then()
{
Check.ThatCode(() => MethodToTest())
.Throws<Exception>()
.WithMessage("Process has been failed");
}
Bu eski bir soru olmasına rağmen, tartışmaya yeni bir düşünce eklemek istiyorum. Beklenecek Yerleştir, Yasa, Talep desenini, Yerleştir, Yasa, Talep'i genişlettim. Beklenen bir istisna işaretçisi yapabilir ve ardından atandığını iddia edebilirsiniz. Bu, Eklerinizi bir catch bloğunda yapmaktan daha temiz hissettirir ve Yasa bölümünüzü çoğunlukla test edilen yöntemi çağırmak için tek bir kod satırı için bırakır. Ayrıca kodda birden fazla noktaya Assert.Fail();
veya return
noktadan da gerek yoktur . Atılan diğer istisnalar, testin başarısız olmasına neden olur, çünkü yakalanmaz ve beklenen türünüzün bir istisnası atılırsa, ancak beklediğiniz kişi değildi, mesaja veya diğer özelliklerine karşı iddiada bulunmak istisna, testinizin yanlışlıkla geçmemesine yardımcı olur.
[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);
}