如何使用NUnit确保Debug.Assert正确触发

Xil*_*nic 6 c# nunit c#-4.0

我一直试图解决这个问题:如何创建一个单元测试来测试函数是否因为Debug.Assert(from System.Diagnostics)失败而中止,在执行此操作时将其标记为已通过,如果不执行则将其标记为失败吨.

我知道NUnit有这个功能[ExpectedException(typeof( ArgumentException ) )],但我似乎无法从MSDN网站上找出它是什么样的例外.Intuition会说它可能类似于AssertionException,而且确实存在......但它是NUnit框架的一部分.我猜这是NUnit断言的例外.我或许可以通过使用以下方式来核对它:

[ExpectedException(typeof(Exception))]
Run Code Online (Sandbox Code Playgroud)

但这会引起标准Windows调试窗口出现的问题.在我的搜索中,我遇到了一些方法来移除这个窗口,但是这就像把屠宰刀带到了通常使用手术刀的手术室.因为我希望能够在出现意外情况时看到这个窗口,当我执行我的程序时.

我想有一种解决方法是用对应方替换Debug.Assert方法NUnit(我现在还在我的项目中,所以它不是一个太大的重构),但我认为很多程序员都坚持使用Debug.Assert.NET中的标准功能. .

因此,我想知道如何"断言" Debug.Assertion失败,而不必从我的项目中"谋杀"Windows调试屏幕?

要在我的代码中有一个合同的具体示例,下面有一个示例.对于那些看似熟悉的东西,它是Warhammer 40K桌面战争游戏中的To-Wound表,作为一个功能.

static public int assaultToHit(int _attacker_WeaponSkill,
            int _defender_WeaponSkill)
        {
            //Preconditions
            Debug.Assert(_attacker_WeaponSkill >= 1 && _attacker_WeaponSkill <= 10,
                "Weapon Skill stat must be in range [1,10]");
            Debug.Assert(_defender_WeaponSkill >= 1 && _defender_WeaponSkill <= 10,
                "Weapon Skill stat must be in range [1,10]");

            int target;
            if (_attacker_WeaponSkill > _defender_WeaponSkill)
            {
                target=3;
            }
            else if (_defender_WeaponSkill >= (_attacker_WeaponSkill + _attacker_WeaponSkill + 1))
            {
                target=5;
            }
            else
            {
                target=4;
            }

            //postconditions
            Debug.Assert(target >= 3 && target <= 5,
                "To hit target for assault must be in range [3,5]");

            return target;
        }
Run Code Online (Sandbox Code Playgroud)

测试前置条件的函数将是这样的:

    [TestCase(-1,2)]
    [TestCase(1, -2)]
    [TestCase(-1, -2)]
    [TestCase(11, 2)]
    [TestCase(1, 20)]
    [TestCase(11, 20)] 
    [ExpectedException(typeof(Exception))]
    public void testContract_AssaultToHit(int _attacker_weaponskill, 
        int _defender_weaponskill)
    {
        Warhammer40kRules.assaultToHit(_attacker_weaponskill, 
            _defender_weaponskill);
    }
Run Code Online (Sandbox Code Playgroud)

sll*_*sll 5

在出现错误时,您似乎使用了错误的工具来控制应用程序流.Debug.Assert()不应该用于驱动应用程序逻辑流程.

单元测试应该涵盖一个真实的测试用例,看起来你要么试图实现错误的测试用例,要么你需要抛出异常/等而不是使用Debug.Assert().您可以共享一些代码,以便为您提供一些具体的建议.

无论如何,您可以阅读有关如何添加自定义跟踪侦听器和拦截Assert调用的MSDN .

有用的链接:


Xil*_*nic 4

/sf/answers/8207321/可以找到一项建议,即使用异常进行公共接口,同时使用断言来验证内部代码。当对函数的前提条件进行单元测试时(类似的想法可以应用于后置条件,尽管我个人更喜欢在那里使用断言),因此可以建议使用异常而不是使用断言。

使用异常而不是会产生如下示例:

static public int assaultToHit(int _attacker_WeaponSkill,
        int _defender_WeaponSkill)
    {
        //Preconditions
        if(!(_attacker_WeaponSkill >= 1 && _attacker_WeaponSkill <= 10))
        {
            throw new ArgumentOutOfRangeException("Attackers WeaponSkill must be in range [1,10]");
        }
        if(!(_defender_WeaponSkill >= 1 && _defender_WeaponSkill <= 10))
        {
            throw new ArgumentOutOfRangeException("Defenders WeaponSkill must be in range [1,10]");
        }

        ...
        //rest unchanged
    }
Run Code Online (Sandbox Code Playgroud)

加上下面的NUnit测试:

[TestCase(-1,2)]
[TestCase(1, -2)]
[TestCase(-1, -2)]
[TestCase(11, 2)]
[TestCase(1, 20)]
[TestCase(11, 20)] 
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void testContract_AssaultToHit(int _attacker_weaponskill, 
    int _defender_weaponskill)
{
    Warhammer40kRules.assaultToHit(_attacker_weaponskill, 
        _defender_weaponskill);
}
Run Code Online (Sandbox Code Playgroud)

侧边栏 [2014 年 6 月 12 日]: 我最近得出这样的观点:您不必在契约设计的背景下测试前提条件违规。如果您在设计时考虑到 DbC,那么您基本上会声明以下内容:“如果您调用一个方法并满足其先决条件,则可以保证一定的行为。如果您不满足该方法的先决条件,则可以预期未定义的行为。”。从“未定义行为”一词的最广泛意义上来说,这意味着您不能期望任何类型的状态。您可能会遇到异常,也许什么也没有发生,也许您的硬盘被格式化(好吧......您明白了;))。因此,您无法测试“未定义的行为”。

您可以测试的是防御性编程,确保前提条件失败不会导致函数运行,例如抛出Exception. 此行为是可测试的。