在 AssertionError 的情况下在“断言”之后执行代码

Posted

技术标签:

【中文标题】在 AssertionError 的情况下在“断言”之后执行代码【英文标题】:Execute code after an 'assert' in case of AssertionError 【发布时间】:2019-11-18 03:39:11 【问题描述】:

我正在测试一个函数,其主要目的是将文件分配到该函数的参数中接收到的文件夹中。为此,我在我的根文件夹中创建了一个空文件并测试一些不同的路径参数。更明确地说,这里有一个例子:

alocate_file('folder1','folder2','folder3', 'file.txt')

此行将导致此位置:

root/Downloads/folder1/folder2/folder3/file.txt

我的函数的一些额外特性:Downloads 文件夹是隐式的,它接收一个列表作为参数,并假定列表中的最后一个字符串是文件。

我的问题

测试此函数后,我删除了空文件(仅为测试目的创建)和我的函数创建的所有文件夹。这是在断言之后使用 shutil.rmtree 完成的,这就是问题所在。 当测试失败时,它会引发一个AssertionError并且那些文件夹和文件不会被删除,因为断言之后的代码没有被执行。这也破坏了其他测试,因为我对所有测试都使用相同名称的文件和文件夹。然后我必须手动删除所有这些文件才能再次正确测试。

我曾想过使用固定装置,但我认为这不是一个好的解决方案,因为正如我所说,它测试不同的路径创建,它没有通用案例。我必须为每个测试创建单独的固定装置,这似乎不是最好的方法。

这是我的一个有这个问题的测试:

def test_alocate_file_three_level_path(root_path):
    # creates files in root
    file_path1 = os.path.join(root_path, 'test1.pdf')
    Path(file_path1).touch()
    # creates path for test
    test_path = os.path.join(root_path, 'Downloads', 'path1', 'path2','path3','test1.pdf')
    # function alocate the file to folders
    func_aux.alocate_file('path1', 'path2', 'path3', 'test1.pdf')
    # check if the file is there
    assert os.path.isfile(test_path) == True
    # remove the created file and folders
    remove_path = os.path.join(root_path, 'Downloads', 'path1')
    shutil.rmtree(remove_path)

我想知道我可以保证删除我为测试目的创建的所有文件夹和文件的唯一方法是为每个测试使用特定的固定装置,还是有一种我可以始终执行的方法断言之后的代码即使有 AssertionError

【问题讨论】:

为什么不能'你做if not os.path.isfile(test_path):然后执行你需要的任何清理代码,如果是这样的话,然后做raise AssertionError 我在这里尝试过,这是最好的方法吗?因为,如果我理解你的建议,我会创建这个 if statament:当为 false 时,我删除文件并引发错误,如果为 true,则“断言 True”(我该怎么做?)并删除文件,重复代码? 您的断言将位于 if/else 的不同分支中,因此您仍然可以断言该文件的存在是 Trueelse 分支中(虽然我不不知道这有多大用处),然后像现在一样继续。我的建议中唯一相关的部分是能够延迟AssertionError 的提升,直到您完成所需的任何清理工作。我并没有真正做很多单元测试,所以我无法告诉你这是否是最好的方式。 不,固定装置到这里的正确方式。否则,您正在使用设置/拆卸代码污染测试,这使得它非常脆弱。使用tmpdir 夹具生成root_pathpytest 将在测试前创建一个临时目录,然后自动清理。 【参考方案1】:

正如@hoefling 建议的那样,我实现了一个创建临时目录的装置。修改我在帖子中提供的代码,结束如下:

@pytest.fixture(scope="module")
def temp_dir(root_path):
    down_path = os.path.join(root_path, 'Downloads', 'temp_dir')
    os.makedirs(down_path)
    yield down_path
    shutil.rmtree(down_path)


def test_alocate_file_three_level_path(root_path, temp_dir):
    # creates files in root
    file_path1 = os.path.join(root_path, 'test1.pdf')
    Path(file_path1).touch()
    # creates path for test
    test_path = os.path.join(temp_dir, 'path1', 'path2','path3','test1.pdf')
    # function alocate the file to folders
    func_aux.alocate_file('temp_dir', 'path1', 'path2', 'path3', 'test1.pdf')
    # check if the file is there
    assert os.path.isfile(test_path) == True

此保证,在测试结束时,所有辅助文件均已删除。对于那些不明白发生了什么的人,fixture 会一直执行到 yield。在那之后,测试获得了它的价值并完成了它的工作。独立于AssertionError,当测试结束时,返回到fixture,在yield之后运行代码。

【讨论】:

我的意思是tmpdir 夹具已经在pytest 中可用,因此您不必自己重新实现它。不过,很高兴您对固定装置有所了解!【参考方案2】:

您可以使用简单的try... finally 块来管理此问题,因为如果断言未得到验证,assert 会引发 AssertionError:

x = 1

try:
    assert x == 0
finally:
    print("this code will always run")

【讨论】:

with,更符合预期的用例。 不需要except 子句。事实上,为了保持测试失败并出现AssertionError 的通常行为,人们不想处理AssertionError(或者想重新提出它)。无论如何,finally 子句中的清理代码都会被执行。 @aichao 是的,你说得对,我会编辑我的答案

以上是关于在 AssertionError 的情况下在“断言”之后执行代码的主要内容,如果未能解决你的问题,请参考以下文章

Scala 编译器中的错误:java.lang.AssertionError:断言失败(即使在 Eclipse 中指定了项目依赖项)

节点:断言:400 抛出错误; ^ AssertionError [ERR_ASSERTION]:指定的回调对象无效

Java基础之断言

kivymd 中的 AssertionError

解决用try except 捕获assert函数产生的AssertionError异常时,导致断言失败的用例在测试报告中通过的问题

断言与异常