代码测试用例入门

在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

通常来说,程序员每修改一次程序就会进行最少一次单元测试,在编写程序的过程中前后很可能要进行多次单元测试,以证实程序达到软件规格书要求的工作目标,没有程序错误;虽然单元测试不是什么必须的,但也不坏,这牵涉到项目管理的政策决定。

每个理想的测试案例独立于其它案例;为测试时隔离模块,经常使用stubs、mock或fake等测试马甲程序。单元测试通常由软件开发人员编写,用于确保他们所写的代码匹配软件需求和遵循开发目标。它的实施方式可以是非常手动的(通过纸笔),或者是做成构建自动化的一部分。

测试的通用规则:

  • 测试单元应该集中于小部分的功能,并且证明它是对的。
  • 每个测试单元应该完全独立。
  • 通过Mock去除依赖
  • 尽量使测试单元快速运行。
  • 实现钩子来持续集成

我们通过一个简单的python程序及unittest作为示例来为大家介绍如何进行测试,这里推荐大家使用python3来运行示例。

我们先创建一个将会使用的测试目录

mkdir /tmp/TestHookTest
cd /tmp/TestHookTest

测试单元应该集中于小部分的功能,并且证明它是对的

下图为unittest包中包含的断言

我们现在来写一个通过用户名获得github信息的一个函数,并对这个函数进行测试

# test.py
import unittest
import json

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

通过python3 -m unittest test运行

每个测试单元应该完全独立

  • 每个都能够单独运行,除了调用的命令,都需在测试套件中。要想实现这个规则,测试单元应该加载最新的数据集,之后再做一些清理。

  • 如果有数据库依赖,在每次测试前创建测试数据库,结束后销毁该数据库,测试应该有单独的数据库,不要在生产和开发环境测试,避免数据变化引起的测试失败

  • 通过Mock去除依赖

假设我们现在想把取得的用户数据保存到本地,并测试是否正确保存

# test.py
import unittest
import json

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

def save_data(data):
    with open('data.json', 'w') as f:
        f.write(json.dumps(data))

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

    def test_save_data(self):
        data = fetch_github_profile('ZhangBohan')
        save_data(data)

        with open('data.json') as f:
            file_data = json.loads(f.read())
            self.assertIsNotNone(file_data)
            self.assertEqual(data['id'], file_data['id'])

在这个测试中我们的test_save_data中的data依赖fetch_github_profile中的返回数据,现实情况中会遇到更为复杂的依赖,为了一个测试用例,我们可能需要构建大量的初始化数据。我们可以通过mock来解除这个依赖,让test_save_data专注于测试保存数据部分

# test.py
import unittest
import json
from unittest.mock import MagicMock

import requests

def fetch_github_profile(username):
    response = requests.get('https://api.github.com/users/' + username)
    return response.json()

def save_data(data):
    with open('data.json', 'w') as f:
        f.write(json.dumps(data))


FAKE_PROFILE_DATA = {
  "login": "ZhangBohan",
  "id": 2317407
}

class SaveDataTest(unittest.TestCase):

    def test_fetch_github_profile(self):
        username = 'ZhangBohan'
        data = fetch_github_profile('ZhangBohan')
        self.assertEqual(data['login'], username)

    def test_save_data(self):
        fetch_github_profile = MagicMock(return_value=FAKE_PROFILE_DATA)
        data = fetch_github_profile('ZhangBohan')
        save_data(data)

        with open('data.json') as f:
            file_data = json.loads(f.read())
            self.assertIsNotNone(file_data)
            self.assertEqual(data['id'], file_data['id'])

尽量使测试单元快速运行

如果一个单独的测试单元需要较长的时间去运行,开发进度将会延迟,测试单元将不能如期常态性运行。有时候,因为测试单元需要复杂的数据结构,并且当它运行时每次都要加载,所以其运行时间较长。把运行吃力的测试单元放在单独的测试组件中,并且按照需要运行其它测试单元。

实现hook来持续集成

通过代码提交的本地hook或者webhook来持续集成测试你的代码。

举个git本地hook的例子(这可假设你了解git hook的工作原理)。

> git init
> vim .git/hooks/pre-commit

.git/hooks/pre-commit文件中写入

#!/bin/sh

cd /tmp/TestHookTest && python3 -m unittest test

执行:

> chmod +x .git/hooks/pre-commit

> git add test.py
> git commit -m "test hook"
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
[master (root-commit) b390117] test hook
 1 file changed, 9 insertions(+)
 create mode 100644 test.py

在远程代码仓库部署的webhook能更好的测试全部代码。

Python指南-测试你的代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,067评论 19 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,821评论 25 709
  • Startup 单元测试的核心价值在于两点: 更加精确地定义某段代码的作用,从而使代码的耦合性更低 避免程序员写出...
    wuwenxiang阅读 10,169评论 1 27
  • 本文试图总结编写单元测试的流程,以及自己在写单元测试时踩到的一些坑。如有遗漏,纯属必然,欢迎补充。 目录概览: 编...
    苏尚君阅读 3,458评论 0 4
  • 很想为自己的青春 大哭一场 刚看到一篇写我高中母校的文章,看到高中的照片,教室,那瞬间,眼泪直逼眼眶。不是太美好的...
    雪梨摇阅读 226评论 0 0