软件测试级别
- Unit tests: 目的是验证软件的每个单元是否按设计运行
- Integration tests: 目的是暴露集成单元之间交互中的故障
- System tests: 目的是评估系统是否符合指定要求
- Acceptance test: 目的是评估系统是否符合业务要求,以及是否可接受交付
什么是 BDD & TDD
行为驱动开发 (BDD)
- 从外到内地描述系统的行为
- 用于集成/验收测试 Integration & Acceptance tests
测试驱动开发(TDD)
- 由内而外测试系统功能
- 用于单元测试 Unit test
BDD 是构建正确的事物,而 TDD 是保证正确的构建
TDD 工作流
Python 测试工具
- PyUnit:这就是我们将使用的。它是像 JUnit 这样的标准单元测试模块
- Green: 类似 nose 的测试运行器,可遵循红/绿/重构(我们也将使用它)
断言 Assertions
Example:
sum = add(2, 3)
assert(sum == 5)
from math import pi
def area_of_circle(radius):
""" Calculates the area given a non-negative number """
if type(radius) not in [int, float]:
raise TypeError("The radius must be a number")
if radius < 0:
raise ValueError("The radius cannot be negative")
return pi * (radius**2)
什么是测试覆盖率?
- 测试覆盖率衡量所有测试期间执行的代码行的百分比
- 高测试覆盖率使人确信已编写的代码已被测试用例覆盖
如何提高测试覆盖率呢?
- 必须包含“happy”路径和“sad”路径才能获得完整的测试覆盖率
- 哪怕覆盖率为100%,也不能保证没有bug产生
For example:
class TestCircleArea(TestCase):
# Happy path
def test_area(self):
""" Test areas when radius is >= 0 """
self.assertAlmostEqual(area_of_circle(1), pi)
self.assertAlmostEqual(area_of_circle(0), 0)
self.assertAlmostEqual(area_of_circle(2.1), pi * 2.1**2)
# Sad path
def test_values(self):
""" Test that ValueError is raised for bad values """
self.assertRaises(ValueError, area_of_circle, -3)
def test_types(self):
""" Test that TypeError is raised with bad types """
self.assertRaises(TypeError, area_of_circle, True)
self.assertRaises(TypeError, area_of_circle, "radius")
Factories and Fakes
- 有时您需要假数据来进行测试
- 有时您会希望整个类都是假数据
Account Model:
db = SQLAlchemy()
class Account(db.Model):
"""
Class that represents an Account
"""
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
email = db.Column(db.String(64))
phone_number = db.Column(db.String(32), nullable=True)
active = db.Column(db.Boolean(), nullable=False, default=True)
date_joined = db.Column(db.DateTime, nullable=False)
Factory:
import factory
from datetime import datetime
from models import Account
class AccountFactory(factory.Factory):
""" Creates fake Accounts """
class Meta:
model = Account
id = factory.Sequence(lambda n: n)
name = factory.Faker("name")
email = factory.Faker("email")
phone_number = factory.Faker("phone_number")
active = factory.FuzzyChoice(choices=[True, False])
date_joined = factory.FuzzyDate(datetime.date(2008, 1, 1)
Using Account Factory:
def test_update_account(self):
""" It should Update an account """
account = AccountFactory()
account.create()
# Fetch it back
account = Account.find(account.id)
account.email = "XYZZY@plugh.com" # Change it
account.update()
# Fetch it back again
account = Account.find(account.id)
self.assertEqual(account.email, "XYZZY@plugh.com")