使用代码库测试调用

测试使用invoke的代码库的策略;一些适用于专注于cli任务的代码,另一些适用于更通用/重构的设置。

子类和修改调用“internals”

一个简短的前言:大多数用户会发现后面的方法是合适的,但是高级用户应该注意到invoke的设计使其本身易于测试。这意味着,在许多情况下,即使调用的“内部”也被暴露为低/不共享的责任,公开文档类,可以被分类和修改,以注入测试友好的值或模拟。一定要检查一下 API documentation 你说什么?

使用 MockContext

为测试目的对invoke的公共api进行子类化的实例是我们自己的 MockContext . 代码基,它围绕 Context 对象及其方法(大多数面向任务的代码)将发现通过注入 MockContext 已实例化以产生部分 Result 物体。

例如,执行以下任务:

from invoke import task

@task
def show_platform(c):
    uname = c.run("uname -s").stdout.strip()
    if uname == 'Darwin':
        print("You paid the Apple tax!")
    elif uname == 'Linux':
        print("Year of Linux on the desktop!")

测试它的一个例子 MockContext 可能如下(注: trap is only one example of a common test framework tactic which mocks sys.stdout/err ):

import sys
from spec import trap
from invoke import MockContext, Result
from mytasks import show_platform

@trap
def test_show_platform_on_mac():
    c = MockContext(run=Result("Darwin\n"))
    show_platform(c)
    assert "Apple" in sys.stdout.getvalue()

@trap
def test_show_platform_on_linux():
    c = MockContext(run=Result("Linux\n"))
    show_platform(c)
    assert "desktop" in sys.stdout.getvalue()

期待 Results

核心调用子流程方法,如 run 全部返回 Result 对象(如上所述)可以仅用部分数据(例如标准输出,但不存在退出代码或标准错误)容易地实例化。

这意味着组织良好的代码可以更容易测试,并且不需要太多的使用。 MockContext 或终端输出模拟。

上一个示例的迭代:

from invoke import task

@task
def show_platform(c):
    print(platform_response(c.run("uname -s")))

def platform_response(result):
    uname = result.stdout.strip()
    if uname == 'Darwin':
        return "You paid the Apple tax!"
    elif uname == 'Linux':
        return "Year of Linux on the desktop!"

现在,大部分实际逻辑都是可测试的,代码行更少,对代码运行的“真实世界”的假设也更少(例如,不需要关心 sys.stdout 完全):

from invoke import Result
from mytasks import platform_response

def test_platform_response_on_mac():
    assert "Apple" in platform_response(Result("Darwin\n"))

def test_platform_response_on_linux():
    assert "desktop" in platform_response(Result("Linux\n"))

避免完全模拟依赖代码路径

这更像是一种通用的软件工程策略,但上述代码示例的自然终点是您的主逻辑根本不关心调用——只关心基本的python(或本地定义的)数据类型。这允许您单独测试逻辑,或者忽略对调用端的测试,或者只为代码与invoke接口的地方编写目标测试。

任务代码的另一个小调整:

from invoke import task

@task
def show_platform(c):
    uname = c.run("uname -s").stdout.strip()
    print(platform_response(uname))

def platform_response(uname):
    if uname == 'Darwin':
        return "You paid the Apple tax!"
    elif uname == 'Linux':
        return "Year of Linux on the desktop!"

还有测试:

from mytasks import platform_response

def test_platform_response_on_mac():
    assert "Apple" in platform_response("Darwin\n")

def test_platform_response_on_linux():
    assert "desktop" in platform_response("Linux\n")