如何测试比较的用法?

2024-09-27 09:33:57 发布

您现在位置:Python中文网/ 问答频道 /正文

如何检验比较是用来得到结果的?你知道吗

x = 42
y = 43
is_identical = id(x) == id(y)

为了确保它不是为了欺骗测试而写的:

is_identical = False

更新

背景是学生们需要将他们的作业上传到一个自动运行单元测试(或pytests)的工具中,向他们展示上传的代码有多好。在一个练习中,他们应该只执行一个比较,而不能通过将True/False直接作为变量赋值来欺骗单元测试。函数尚未使用。你知道吗


Tags: 工具函数代码idfalsetrueis作业
3条回答

我对answer from amanb很满意。不过,在此期间,我也找到了一个解决办法,给了我很好的效果。我用^{}代替了库^{}。你知道吗

def testcase_ok():
    import ast
    to_proof = "is_identical = id(x) == id(y)"  # will pass
    assert any(isinstance(node, ast.Eq) for node in ast.walk(ast.parse(to_proof)))


def testcase_fail():
    import ast
    to_proof = "is_identical = False"  # will fail
    assert any(isinstance(node, ast.Eq) for node in ast.walk(ast.parse(to_proof)))

此外,这是获得源代码相关部分的一种方法,无需硬编码is_identical = …(为了清楚起见,如果给定了变量,则无需检查):

import inspect
import file_to_test as submission

codelines = inspect.getsourcelines(submission)[0]
to_proof = [line.strip() for line in codelines if line.strip().lower().startswith("is_identical")][-1]

这个恶作剧的猴子补丁似乎解决了:

# test_foo.py

from unittest import TestCase
from unittest.mock import patch

from importlib import reload

class FooTest(TestCase):

    def test_comparison_used(self):
        import foo
        self.assertFalse(foo.is_identical)
        with patch("foo.id") as id_:
            reload(foo)
            id_().__eq__.assert_called()

foo.py使用“作弊”方法时:

#!/usr/bin/env python

x = 42
y = 43
is_identical = False # id(x) == id(y)

我得到E AssertionError: Expected '__eq__' to have been called.

但是如果我做了is_identical = id(x) == id(y),那么测试就通过了。你知道吗

下面的解决方案使用代码对象中的常量来标识bool值True/False,并使用字节码指令'COMPARE_OP'来标识比较操作。可以对代码进行改进以添加更多案例,但这只是给您一个开始。为了显示所有条件,我将函数中的所有参数都设置为可选:

In [130]: def catch_trick_bool(unit_test_result=None, student_compare=None,student_trick=None):
     ...:     student = str(input("Enter student name: "))
     ...:     # import dis - Use this to further examine code objects.
     ...:     # Eg. use  dis.dis(co_unit_test) to view the code objects for 
     ...:     # the unit test expresssion
     ...:     if unit_test_result:
     ...:         co_unit_test = compile(unit_test_result, 'none', 'single')
     ...:         if False in co_unit_test.co_consts:
     ...:             print("Bool value 'False' returned from Unit Test")
     ...:     elif student_compare:
     ...:         co_student_compare = compile(student_compare, 'none', 'single')
     ...:         if '6b' in co_student_compare.co_code.hex():
     ...:             print("Student {} performed the comparison successfully".format(student))
     ...:     else:
     ...:         co_student_trick = compile(student_trick, 'none', 'single')
     ...:         if False in co_student_trick.co_consts:
     ...:             print("Student {} tried to set a bool value of 'False' for the test".format(student))
     ...:

In [131]: catch_trick_bool(unit_test_result='is_identical_unit_test = False')
Enter student name: John
Bool value 'False' returned from Unit Test

In [132]: catch_trick_bool(student_trick='is_identical_student_trick = False')
Enter student name: John
Student John tried to set a bool value of 'False' for the test

In [133]: catch_trick_bool(student_compare='id(x) == id(y)')
Enter student name: John
Student John performed the comparison successfully

解释:

下面显示如何在代码对象的字节码指令的十六进制表示中搜索十六进制字节码0x6b。这个字节码对应于opname'COMPARE_OP'。此指令指示代码中的比较操作。你知道吗

In [137]: co = compile('is_identical = id(x) == id(y)', 'none', 'single')

In [138]: type(co)
Out[138]: code

In [139]: dis.dis(co)
  1           0 LOAD_NAME                0 (id)
              2 LOAD_NAME                1 (x)
              4 CALL_FUNCTION            1
              6 LOAD_NAME                0 (id)
              8 LOAD_NAME                2 (y)
             10 CALL_FUNCTION            1
             12 COMPARE_OP               2 (==)
             14 STORE_NAME               3 (is_identical)
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE

In [140]: co.co_code
Out[140]: b'e\x00e\x01\x83\x01e\x00e\x02\x83\x01k\x02Z\x03d\x00S\x00'

In [141]: co.co_code.hex()
Out[141]: '6500650183016500650283016b025a0364005300'

In [142]: dis.opname[0x6b]
Out[142]: 'COMPARE_OP'

In [143]: '6b' in co.co_code.hex()
Out[143]: True

In [144]: co2 = compile('is_identical = 5 > 2', 'none', 'single')

In [145]: co2.co_code.hex()
Out[145]: '640064016b045a0064025300'

In [146]: '6b' in co2.co_code.hex()
Out[146]: True

类似地,bool值被解析为LOAD_CONST字节码指令,这些指令可以通过co_consts轻松访问:

In [147]: co3 = compile('is_identical = False', 'none', 'single')

In [148]: dis.dis(co3)
  1           0 LOAD_CONST               0 (False)
              2 STORE_NAME               0 (is_identical)
              4 LOAD_CONST               1 (None)
              6 RETURN_VALUE

In [149]: co3.co_consts
Out[149]: (False, None)

In [150]: False in co3.co_consts
Out[150]: True

结论:

分解的代码对象可以帮助识别布尔值的来源。如果在上述情况下显式定义了bool值,那么bool值将被解析为常量LOAD_CONST字节码指令。如果它是通过使用比较的布尔表达式(如5>;2)隐式推导的,则代码将解析为特定的字节码指令COMPARE_OP,以指示比较操作。您可以进一步扩展代码以检查truthy值,并根据需要进行修改。你知道吗

相关问题 更多 >

    热门问题