如何测试函数是否不会抛出异常?

4
我有这个函数和测试:
public void SaveForWeb ()
{
    UpdateGameState();
    try
    {
        PlayerPrefs.SetFloat(Helper.EXP_KEY, experience);
        PlayerPrefs.SetFloat(Helper.SCORE_KEY, score);
        // other properties that need to be saved in PlayerPrefs

        PlayerPrefs.Save();
    }
    catch (Exception ex)
    {
        Debug.Log(ex.Message);
    }
}

[Test]
[Category(Helper.TEST_CATEGORY_SAVE_FOR_WEB)]
public void SaveForWebTest ()
{
    // arrange
    var slgdController = FakeSaveLoadGameDataController();
    TestDelegate myDelegate = () => {};

    // act
    slgdController.SaveForWeb();

    // assert
    Assert.DoesNotThrow(myDelegate);
}

但是我感觉这个断言和SaveForWeb()函数的调用之间没有关联。

注意:SaveForWeb()使用Unity3D API中的PlayerPrefs,如果本地文件超过1MB可能会抛出PlayerPrefsException异常。

这种方式是否正确断言函数不会抛出异常?


在单元测试中,您不需要断言函数不会抛出异常。默认情况下,如果单元测试中抛出异常,那么单元测试将由于意外异常而失败。 - Matthew Watson
1
是的,这是一个重复的内容,但没关系,Unity3D社区的某些人可能会发现它有用。 - Vlad
如果重复有用的话(并且是无害的),那么就没有问题 :p - Drew
我读了下面的xUnit问题,真是辣眼睛。简而言之,他们确实摆脱了DoesNotThrow。我想我属于有主见的阵营,认为它应该留下来,但现在为时已晚。对于那些想要在测试中明确声明此事的人,您还可以使用Record.Exception或尝试获取异常来检查这一点-如果没有异常发生,它将返回null: var exception = Record.Exception(() => act()); Assert.Null(exception); - Tabrock
5个回答

7
如果方法没有抛出异常,您不需要进行断言。我知道您正在使用NUnit,但有一个xUnit issue描述了为什么您不需要进行断言。
但是,如果您想要明确表达,可以这样做:
[Test]
[Category(Helper.TEST_CATEGORY_SAVE_FOR_WEB)]
public void SaveForWebTest ()
{
    // arrange
    var slgdController = FakeSaveLoadGameDataController();

    Assert.DoesNotThrow(() => slgdController.SaveForWeb());
}

其他答案也可以,但是这种方法在失败时提供了最好的错误信息。 - Charlie

6

如果代码没有抛出异常,那么它应该可以正常运行并到达测试方法的结尾。因此,您可以在结尾处添加一个Assert.IsTrue(true),仅在方法无误地运行时才会调用。否则(如果发生异常),测试将立即停止并返回false。

编辑:实际上您甚至不需要调用Assert.IsTrue,如果测试能够到达结尾,它也将成功。


那么你想说的是,如果SaveForWeb()抛出异常,它会在中断的那一行停止测试的执行? - Vlad
是的,如果发生异常,测试将立即停止并返回false。 - MakePeaceGreatAgain

4
在我的测试中,我没有做任何断言。如果抛出异常,测试将失败,因为您没有ExpectedException属性或类似的东西。
在我看来,Assert.IsTrue(true) 是没有意义的。

3

正如其他人所提到的,单元测试将自动失败。但是,如果您确实想为此编写断言,请尝试以下内容。

[Test]
[Category(Helper.TEST_CATEGORY_SAVE_FOR_WEB)]
public void SaveForWebTest ()
{
    // arrange
    var slgdController = FakeSaveLoadGameDataController();
    TestDelegate myDelegate = () => {};

    // act
    try
    {
        slgdController.SaveForWeb();
        Assert.IsTrue(true) // Not Actually necessary as should still pass
    }
    catch (Exception ex)
    {
        Assert.Fail("Expected no exception, but got: " + ex.Message);
    }
}

1
你应该给单元测试起更有意义的名称。这样,你可以从测试中实际了解出现了什么问题:
    [TestMethod]
    public void SaveForWeb_WhenGameControllerIsOk_DoesNotThrowException()
    {
        // Arrange
        var controller = FakeSaveLoadGameDataController();

        // Act
        controller.SaveForWeb();

        // Assert - Will fail by exceptionThrown
    }

    [TestMethod, ExpectedException(typeof(ReallyBadException))]
    public void SaveForWeb_WhenGameControllerThrowsException_ThrowsException()
    {
        // Arrange
        var controller = new FakeSaveLoadGameDataControllerWithException();

        // Act
        controller.SaveForWeb();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接