为整个仓库动态创建测试文件模板 - python

我一直在环顾四周,但找不到任何可以满足我想要的东西。

我想知道是否有实用程序可以扫描整个存储库的结构和源代码,并创建一个尚不存在的并行测试结构,其中代码中的每个函数和方法都具有等效的空单元测试。

必须手动编写一堆单元测试样板非常繁琐。

例如,假定此项目结构:

myproject
|--src
   |--__init__.py
   |--a.py
   |--subpackage
      |--__init__.py
      |--b.py
      |--c.py

它应该创建:

myproject
|--src
|  |--__init__.py
|  |--a.py
|  |--subpackage
|     |--__init__.py
|     |--b.py
|     |--c.py
|
|--tests
   |--test_a.py
   |--subpackage
      |--test_b.py
      |--test_c.py

并且如果a.py的内容是:

class Printer:
    def print_normal(self, text):
        print(text)

    def print_upper(self, text):
        print(str(text).upper())

    def print_lower(self, text):
        print(str(text).lower())

def greet():
    print("Hi!")

test_a.py的内容应与此类似:

import pytest
from myproject.src import a

def test_Printer_print_normal():
    assert True

def test_Printer_print_upper():
    assert True

def test_Printer_print_lower():
    assert True

def test_greet():
    assert True

有人知道有任何类似这样的python项目吗?即使不完全相同,最初为具有数百个类和数千种方法的大型仓库设置pytest样板时,任何可以节省一些工作的东西都将节省大量时间。

提前致谢。

参考方案

我自己在Python中搜索测试生成器工具,我只能找到那些生成unittest样式类的工具:

pythoscope

从Github安装最新版本:

$ pip2 install git+https://github.com/mkwiatkowski/pythoscope

从理论上看起来很有希望:在模块中基于静态代码分析生成类,将项目结构映射到tests dir(每个库模块一个测试模块),每个功能都有自己的测试类。这个项目的问题在于它几乎被废弃了:当遇到向后移植到Python 2的功能时,没有对Python 3的支持会失败,因此,现在的IMO无法使用。那里有pull requests声称增加了对Python 3的支持,但是那时它们对我不起作用。

不过,如果您的模块使用Python 2语法,则将生成以下内容:

$ pythoscope --init .
$ pythoscope spam.py
$ cat tests/test_spam.py
import unittest


class TestPrinter(unittest.TestCase):
    def test_print_lower(self):
        # printer = Printer()
        # self.assertEqual(expected, printer.print_lower())
        assert False  # TODO: implement your test here

    def test_print_normal(self):
        # printer = Printer()
        # self.assertEqual(expected, printer.print_normal())
        assert False  # TODO: implement your test here

    def test_print_upper(self):
        # printer = Printer()
        # self.assertEqual(expected, printer.print_upper())
        assert False  # TODO: implement your test here

class TestGreet(unittest.TestCase):
    def test_greet(self):
        # self.assertEqual(expected, greet())
        assert False  # TODO: implement your test here

if __name__ == '__main__':
    unittest.main()

螺旋钻

从PyPI安装:

$ pip install auger-python

根据运行时行为生成测试。虽然它可能是带有命令行界面的工具的一种选择,但它需要为库编写一个入口点。即使使用工具,它也只会针对明确要求的内容生成测试;如果未执行功能,则不会为其生成测试。这使得它只能部分用于工具(最坏的情况是,您必须多次运行该工具,并且必须激活所有选项才能覆盖完整的代码库),并且几乎无法与库一起使用。

不过,这是Auger从示例入口点为您的模块生成的内容:

# runner.py

import auger
import spam

with auger.magic([spam.Printer], verbose=True):
    p = spam.Printer()
    p.print_upper()

执行runner.py产生:

$ python runner.py
Auger: generated test: tests/test_spam.py
$ cat tests/test_spam.py
import spam
from spam import Printer
import unittest


class SpamTest(unittest.TestCase):
    def test_print_upper(self):
        self.assertEqual(
            Printer.print_upper(self=<spam.Printer object at 0x7f0f1b19f208>,text='fizz'),
            None
        )


if __name__ == "__main__":
    unittest.main()

定制工具

对于一项一次性工作,编写自己的AST访问者应该很容易,该访问者可以从现有模块生成测试存根。下面的示例脚本testgen.py使用与pythoscope相同的思想生成简单的测试存根。用法示例:

$ python -m testgen spam.py 
class TestPrinter:
    def test_print_normal(self):
        assert False, "not implemented"


    def test_print_upper(self):
        assert False, "not implemented"


    def test_print_lower(self):
        assert False, "not implemented"


def test_greet():
    assert False, "not implemented"

testgen.py的内容:

#!/usr/bin/env python3

import argparse
import ast
import pathlib


class TestModuleGenerator(ast.NodeVisitor):

    linesep = '\n'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.imports = set()
        self.lines = []
        self.indent = 0
        self.current_cls = None

    @property
    def code(self):
        lines = list(self.imports) + [self.linesep] + self.lines
        return self.linesep.join(lines).strip()

    def visit_FunctionDef(self, node: ast.FunctionDef):
        arg_self = 'self' if self.current_cls is not None else ''
        self.lines.extend([
            '    ' * self.indent + f'def test_{node.name}({arg_self}):',
            '    ' * (self.indent + 1) + 'assert False, "not implemented"',
            self.linesep,
        ])
        self.generic_visit(node)

    def visit_ClassDef(self, node: ast.ClassDef):
        clsdef_line = '    ' * self.indent + f'class Test{node.name}:'
        self.lines.append(clsdef_line)
        self.indent += 1
        self.current_cls = node.name
        self.generic_visit(node)
        self.current_cls = None
        if self.lines[-1] == clsdef_line:
            self.lines.extend([
                '  ' * self.indent + 'pass',
                self.linesep
            ])
        self.indent -= 1

    def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef):
        self.imports.add('import pytest')
        self.lines.extend([
            '    ' * self.indent + '@pytest.mark.asyncio',
            '    ' * self.indent + f'async def test_{node.name}():',
            '    ' * (self.indent + 1) + 'assert False, "not implemented"',
            self.linesep,
        ])
        self.generic_visit(node)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'module',
        nargs='+',
        default=(),
        help='python modules to generate tests for',
        type=lambda s: pathlib.Path(s).absolute(),
    )
    modules = parser.parse_args().module
    for module in modules:
        gen = TestModuleGenerator()
        gen.visit(ast.parse(module.read_text()))
        print(gen.code)

在返回'Response'(Python)中传递多个参数 - python

我在Angular工作,正在使用Http请求和响应。是否可以在“响应”中发送多个参数。角度文件:this.http.get("api/agent/applicationaware").subscribe((data:any)... python文件:def get(request): ... return Response(seriali…

Python exchangelib在子文件夹中读取邮件 - python

我想从Outlook邮箱的子文件夹中读取邮件。Inbox ├──myfolder 我可以使用account.inbox.all()阅读收件箱,但我想阅读myfolder中的邮件我尝试了此页面folder部分中的内容,但无法正确完成https://pypi.python.org/pypi/exchangelib/ 参考方案 您需要首先掌握Folder的myfo…

如何修复AttributeError:模块'numpy'没有属性'square' - python

Improve this question 我已经将numpy更新为1.14.0。我使用Windows10。我尝试运行我的代码,但出现此错误: AttributeError:模块“ numpy”没有属性“ square”这是我的进口商品:%matplotlib inline import matplotlib.pyplot as plt import ten…

R'relaimpo'软件包的Python端口 - python

我需要计算Lindeman-Merenda-Gold(LMG)分数,以进行回归分析。我发现R语言的relaimpo包下有该文件。不幸的是,我对R没有任何经验。我检查了互联网,但找不到。这个程序包有python端口吗?如果不存在,是否可以通过python使用该包? python参考方案 最近,我遇到了pingouin库。

AttributeError:'AnonymousUserMixin'对象没有属性'can' - python

烧瓶学习问题为了定制对匿名用户的要求,我在模型中设置了一个类: class MyAnonymousUser(AnonymousUserMixin): def can(self, permissions): return False def is_administrator(self): return False login_manager.anonymous…