如何测试比较的用法?

时间:2019-04-29 12:27:12

标签: python python-3.x unit-testing pytest

如何测试使用比较来获得结果?

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

并确保没有编写这样的东西来欺骗测试:

is_identical = False

更新

背景是学生需要将其作业上传到自动运行单元测试(或pytest)的工具中,以向他们展示上传的代码的质量。在一个练习中,他们应该只进行比较,而不能通过直接将True / False编写为变量赋值来欺骗单元测试。功能尚未使用。

3 个答案:

答案 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]