如何测试使用比较来获得结果?
x = 42
y = 43
is_identical = id(x) == id(y)
并确保没有编写这样的东西来欺骗测试:
is_identical = False
更新:
背景是学生需要将其作业上传到自动运行单元测试(或pytest)的工具中,以向他们展示上传的代码的质量。在一个练习中,他们应该只进行比较,而不能通过直接将True / False编写为变量赋值来欺骗单元测试。功能尚未使用。
答案 0 :(得分:2)
这种骇人听闻的猴子拼布似乎可以解决:
@ManagedBean
public class DashboardManager {
@PostConstruct
public void init() {
findPendingCustomerOrders(); // hits DB and fetch data
findDeliverables();// fetch data
findProductBelowMinStock(); // fetch data
// some more methods fetching data from DB
}
}
当# 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.
,则测试通过。
答案 1 :(得分:1)
以下解决方案利用代码对象中的常量来标识布尔值True / False,并使用字节码指令'COMPARE_OP'
来标识比较操作。可以对代码进行改进以添加更多的案例,但这只是为了让您获得一个良好的开端。为了显示所有条件,我将函数中的所有args设置为可选:
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
。该字节码对应于操作名'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
类似地,布尔值被解析为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
结论:
反汇编的代码对象可以帮助标识布尔值的来源。如果在上述情况下进行了明确定义,则布尔值将解析为常量LOAD_CONST
字节码指令。如果通过使用比较的布尔表达式(例如5> 2)隐式推导得出,则代码将解析为特定的字节码指令COMPARE_OP
,以指示比较操作。您可以进一步扩展代码以检查真实值,并根据需要进行修改。
答案 2 :(得分:0)
我对answer from amanb感到满意。但是,与此同时,我也找到了可以给我带来良好结果的解决方案。我使用的是dis
而不是库ast
。
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]