Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用Assert测试异常以确保抛出异常的最佳方法_C#_.net_Unit Testing - Fatal编程技术网

C# 使用Assert测试异常以确保抛出异常的最佳方法

C# 使用Assert测试异常以确保抛出异常的最佳方法,c#,.net,unit-testing,C#,.net,Unit Testing,您认为这是测试异常的好方法吗?有什么建议吗 Exception exception = null; try{ //I m sure that an exeption will happen here } catch (Exception ex){ exception = ex; } Assert.IsNotNull(exception); 我正在使用MS Test。使用ExpectedException属性标记测试(这是NUnit或MSTest中的术语;其他单元测试框架的用户

您认为这是测试异常的好方法吗?有什么建议吗

Exception exception = null;
try{
    //I m sure that an exeption will happen here
}
catch (Exception ex){
    exception = ex;
}

Assert.IsNotNull(exception);

我正在使用MS Test。

使用ExpectedException属性标记测试(这是NUnit或MSTest中的术语;其他单元测试框架的用户可能需要翻译)。

对于大多数.net单元测试框架,您可以在测试方法上添加[ExpectedException]属性。但是,这不能告诉您异常发生在您预期的时间点。那是我能帮忙的地方

使用xunit,您可以使用Assert.Throws,这样您就可以执行以下操作:

    [Fact]
    public void CantDecrementBasketLineQuantityBelowZero()
    {
        var o = new Basket();
        var p = new Product {Id = 1, NetPrice = 23.45m};
        o.AddProduct(p, 1);
        Assert.Throws<BusinessException>(() => o.SetProductQuantity(p, -3));
    }
ExceptionThrower callStartOp = delegate(){ testObj.StartOperation(); };

// Check exception is thrown correctly...
AssertThrowsException(callStartOp, typeof(InvalidOperationException), "StartOperation() called when not ready.");

testObj.Ready = true;

// Check exception is now not thrown...
AssertDoesNotThrowException(callStartOp);
[事实]
public void CantDecrementBasketLineQuantityBelowZero()以下
{
var o=新篮子();
var p=新产品{Id=1,净价格=2345万};
o、 AddProduct(p,1);
抛出(()=>o.SetProductQuantity(p,-3));
}

[事实]是[TestMethod]的xunit等价物。

我使用了两种不同的模式。在预期出现异常的大多数情况下,我都使用
ExpectedException
属性。这在大多数情况下是足够的,但是,在某些情况下这是不够的。异常可能不可捕获—因为它是由反射调用的方法引发的—或者我只是想检查其他条件是否成立,比如回滚了事务或者仍然设置了某个值。在这些情况下,我将其包装在一个
try/catch
块中,该块期望出现准确的异常,如果代码成功,则执行
Assert.Fail
,并捕获一般异常,以确保不会引发其他异常

第一种情况:

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MethodTest()
{
     var obj = new ClassRequiringNonNullParameter( null );
}
第二种情况:

[TestMethod]
public void MethodTest()
{
    try
    {
        var obj = new ClassRequiringNonNullParameter( null );
        Assert.Fail("An exception should have been thrown");
    }
    catch (ArgumentNullException ae)
    {
        Assert.AreEqual( "Parameter cannot be null or empty.", ae.Message );
    }
    catch (Exception e)
    {
        Assert.Fail(
             string.Format( "Unexpected exception of type {0} caught: {1}",
                            e.GetType(), e.Message )
        );
    }
}

作为使用
ExpectedException
属性的替代方法,我有时会为测试类定义两种有用的方法:

断言throwsexception()
接受一个委托,并断言它会抛出预期的异常和预期的消息

AssertDoesNotThrowException()
接受同一委托并断言它不会引发异常

当您想测试在一种情况下是否引发异常,但在另一种情况下却没有引发异常时,此配对非常有用

使用它们,我的单元测试代码可能如下所示:

    [Fact]
    public void CantDecrementBasketLineQuantityBelowZero()
    {
        var o = new Basket();
        var p = new Product {Id = 1, NetPrice = 23.45m};
        o.AddProduct(p, 1);
        Assert.Throws<BusinessException>(() => o.SetProductQuantity(p, -3));
    }
ExceptionThrower callStartOp = delegate(){ testObj.StartOperation(); };

// Check exception is thrown correctly...
AssertThrowsException(callStartOp, typeof(InvalidOperationException), "StartOperation() called when not ready.");

testObj.Ready = true;

// Check exception is now not thrown...
AssertDoesNotThrowException(callStartOp);
又漂亮又整洁,嗯

My
AssertThrowException()
AssertDoesNotThrowException()
方法在公共基类上定义如下:

protected delegate void ExceptionThrower();

/// <summary>
/// Asserts that calling a method results in an exception of the stated type with the stated message.
/// </summary>
/// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
/// <param name="expectedExceptionType">The expected type of the exception, e.g. typeof(FormatException).</param>
/// <param name="expectedExceptionMessage">The expected exception message (or fragment of the whole message)</param>
protected void AssertThrowsException(ExceptionThrower exceptionThrowingFunc, Type expectedExceptionType, string expectedExceptionMessage)
{
    try
    {
        exceptionThrowingFunc();
        Assert.Fail("Call did not raise any exception, but one was expected.");
    }
    catch (NUnit.Framework.AssertionException)
    {
        // Ignore and rethrow NUnit exception
        throw;
    }
    catch (Exception ex)
    {
        Assert.IsInstanceOfType(expectedExceptionType, ex, "Exception raised was not the expected type.");
        Assert.IsTrue(ex.Message.Contains(expectedExceptionMessage), "Exception raised did not contain expected message. Expected=\"" + expectedExceptionMessage + "\", got \"" + ex.Message + "\"");
    }
}

/// <summary>
/// Asserts that calling a method does not throw an exception.
/// </summary>
/// <remarks>
/// This is typically only used in conjunction with <see cref="AssertThrowsException"/>. (e.g. once you have tested that an ExceptionThrower
/// method throws an exception then your test may fix the cause of the exception and then call this to make sure it is now fixed).
/// </remarks>
/// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
protected void AssertDoesNotThrowException(ExceptionThrower exceptionThrowingFunc)
{
    try
    {
        exceptionThrowingFunc();
    }
    catch (NUnit.Framework.AssertionException)
    {
        // Ignore and rethrow any NUnit exception
        throw;
    }
    catch (Exception ex)
    {
        Assert.Fail("Call raised an unexpected exception: " + ex.Message);
    }
}
protected delegate void exception rower();
/// 
///断言调用方法会导致声明类型的异常以及声明的消息。
/// 
///调用要测试的方法的委托。
///异常的预期类型,例如typeof(FormatException)。
///预期的异常消息(或整个消息的片段)
受保护的无效资产ThrowException(ExceptionRower ExceptionRowingFunc,类型expectedExceptionType,字符串expectedExceptionMessage)
{
尝试
{
例外RowingFunc();
Assert.Fail(“调用未引发任何异常,但应引发一个异常。”);
}
catch(NUnit.Framework.AssertionException)
{
//忽略并重新显示NUnit异常
投掷;
}
捕获(例外情况除外)
{
IsInstanceOfType(expectedExceptionType,例如,“引发的异常不是预期的类型”);
Assert.IsTrue(例如Message.Contains(expectedExceptionMessage),“引发的异常不包含预期的消息。预期=\”“+expectedExceptionMessage+”\,获取\”“+ex.Message+”\);
}
}
/// 
///断言调用方法不会引发异常。
/// 
/// 
///这通常仅与一起使用。(例如,一旦您测试了一个例外Rower
///方法引发异常,然后您的测试可能会修复异常的原因,然后调用此方法以确保它现在已修复)。
/// 
///调用要测试的方法的委托。
受保护的无效资产不存在异常(例外Rower例外RowingFunc)
{
尝试
{
例外RowingFunc();
}
catch(NUnit.Framework.AssertionException)
{
//忽略并重新显示任何NUnit异常
投掷;
}
捕获(例外情况除外)
{
Assert.Fail(“调用引发意外异常:“+ex.Message”);
}
}

我是新来的,没有评论或否决的名声,但我想指出以下示例中的一个缺陷:

在我熟悉的所有单元测试框架中,
Assert.Fail
通过抛出异常来工作,因此泛型捕获实际上会掩盖测试失败。如果
somethingthausesanexception()
没有抛出,则将抛出
Assert.Fail
,但这不会向测试运行程序冒泡以指示失败

如果您需要捕获预期的异常(即,断言某些细节,如异常上的消息/属性),那么捕获特定的预期类型而不是基本异常类很重要。这将允许出现
Assert.Fail
异常(假设您没有抛出与单元测试框架相同类型的异常),但仍然允许验证由
somethingthausesanception()引发的异常
方法。

自v起,对于测试异常,具有以下方法级别
Assert
s:

,将测试确切的异常类型:

Assert.Throws<NullReferenceException>(() => someNullObject.ToString());
另外,在调试抛出异常的单元测试时,您可能希望防止VS

编辑

下面仅举一个Matthew评论的例子,泛型
Assert.Throws
Assert.Catch
的返回是异常类型的异常,您可以对其进行进一步检查:

// The type of ex is that of the generic type parameter (SqlException)
var ex = Assert.Throws<SqlException>(() => MethodWhichDeadlocks());
Assert.AreEqual(1205, ex.Number);
//ex的类型是泛型类型参数(SqlException)的类型
var ex=Assert.Throws(()=>methodwhichheadlocks());
断言.AreEqual(1205,例如。
public static class AssertException
{
    public static void Throws<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
            return;
        }
        Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
    }

    public static void Throws<TException>(Action action, string expectedMessage) where TException : Exception
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
            Assert.AreEqual(expectedMessage, ex.Message, "Expected exception with a message of '" + expectedMessage + "' but exception with message of '" + ex.Message + "' was thrown instead.");
            return;
        }
        Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
    }
}
AssertException.Throws<ArgumentNullException>(() => classUnderTest.GetCustomer(null));
Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());

//async version
await Assert.ThrowsExceptionAsync<SomeException>(
  () => myObject.SomeMethodAsync()
);
[Test]
[TestCase(null)]
public void FooCalculation_InvalidInput_ShouldThrowArgumentNullExeption(string text)
{
    var foo = new Foo();
    Assert.That(() => foo.Calculate(text), Throws.ArgumentNullExeption);

    //Or:
    Assert.That(() => foo.Calculate(text), Throws.Exception.TypeOf<ArgumentNullExeption>);
}
var result = await Assert.ThrowsExceptionAsync<HttpRequestException>(async ()=>
{
    await myService.SomeMethodAsync("test value");
}

Assert.AreEqual("Response status code does not indicate success: 401 (Unauthorized).", result);