IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 11 - 测试代码 -> 正文阅读

[开发测试]11 - 测试代码

需求:

1、测试新增的功能。

2、测试修改后的代码,以及要确保原有功能不受影响,需要测试原有功能。

使用Python模块unittest。

编写测试用例。

11.1 测试函数

被测函数name_function.py:

def get_formatted_name(first, last):
    """Generate a neatly formatted full name."""
    full_name = f"{first} {last}"
    return full_name.title()

?使用该函数的程序names.py:

from name_function import get_formatted_name

print("Enter 'q' at any time to quit.")
while True:
    first = input("\nPlease give me a first name: ")
    if first == 'q':
        break
    last = input("Please give me a last name: ")
    if last == 'q':
        break
        
    formatted_name = get_formatted_name(first, last)
    print(f"\tNeatly formatted name: {formatted_name}.")

?用户可输入一系列名和姓,并看到格式整洁的姓名:

E:\chapter_11>python names.py
Enter 'q' at any time to quit.

Please give me a first name: xixi
Please give me a last name: haha
        Neatly formatted name: Xixi Haha.

Please give me a first name: q

????????现在假设要修改get_formatted_name(),但保留原有功能:处理只含有名和姓的姓名的方式。为此,可在每次修改get_formatted_name()后都对原有功能进行测试:运行程序names.py,并输入像Janis Joplin这样的姓名。不过这太烦琐了。所幸Python提供了一种自动测试函数输出的高效方式。

11.1.1 单元测试和测试用例

Python标准库中的模块unittest提供了代码测试工具。

单元测试用于核实函数的某个方面没有问题。

测试用例是一组单元测试,它们一道核实函数在各种情形下的行为都符合要求。

良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形的测试。

全覆盖的测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。

对于大型项目,最初只要针对代码的重要行为编写测试即可,等项目被广泛使用时再考虑全覆盖。

11.1.2 可通过的测试

要为函数编写测试用例,可先导入模块unittest和要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。

下面的测试用例只包含一个方法,它检查函数get_formatted_name()在给定名和姓时能否正确工作:test_name_function.py

import unittest

from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    """Tests for 'name_function.py'."""
    
    def test_first_last_name(self):
        """Do names like 'Janis Joplin' work?"""
        formatted_name = get_formatted_name('janis', 'joplin')
#将formatted_name的值与字符串'Janis Joplin'比较。如果它们相等,那么万事大吉;如果它们不相等,就告诉我一声!
        self.assertEqual(formatted_name, 'Janis Joplin')


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

NamesTestCase的类,用于包含一系列针对get_formatted_name()的单元测试。这个类可以随意命名,但最好让它看起来与要测试的函数相关并包含Test字样,必须继承unittest.TestCase类。

运行test_name_function.py时,所有以test_打头的方法都将自动运行。

断言方法核实得到的结果是否与期望的结果一致。

特殊变量__name__:在程序执行时设置的。如果这个文件作为主程序执行,将被设置为'__main__'。在这里,调用unittest.main()来运行测试用例。如果这个文件被测试框架导入,将不是'__main__',因此不会调用unittest.main()。

运行test_name_function.py时,得到的输出如下:

.    #有一个测试通过了
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

修改get_formatted_name()后,可再次运行这个测试用例。如果它通过了,就表明给定Janis Joplin这样的姓名时,该函数依然能够正确地处理。

11.1.3 未通过的测试

下面修改函数get_formatted_name(),它要求通过一个实参指定中间名:name_function.py

def get_formatted_name(first, middle,last):
    """Generate a neatly formatted full name."""
    full_name = f"{first} {middle} {last}"
    return full_name.title()

运行测试用例后,输出如下:

E    #只有一个字母E,指出测试用例中有一个单元测试导致了错误。
======================================================================
#NamesTestCase中的test_first_last_name()导致了错误。
#测试用例包含众多单元测试时,知道哪个测试未通过至关重要。
ERROR: test_first_last_name (__main__.NamesTestCase)    
Do names like 'Janis Joplin' work?
----------------------------------------------------------------------
Traceback (most recent call last):
  File "E:\。。。\chapter_11\test_name_function.py", line 10, in test_first_last_name
    formatted_name = get_formatted_name('janis', 'joplin')
TypeError: get_formatted_name() missing 1 required positional argument: 'last'

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)
[Finished in 316ms]

11.1.4 测试未通过时怎么办

测试未通过时,不要修改测试,而应修复导致测试不能通过的代码:检查刚刚对函数所做的修改,找出导致函数行为不符合预期的修改。

下面来修改get_formatted_name(),将中间名设置为可选的,然后再次运行这个测试用例。

def get_formatted_name(first, last, middle=''):
    """Generate a neatly formatted full name."""
    if middle:
        full_name = f"{first} {middle} {last}"
    else:
        full_name = f"{first} {last}"
    return full_name.title()

?我们无须手工测试这个函数。

11.1.5 添加新测试

确定get_formatted_name()又能正确处理简单的姓名后,我们再编写一个测试,用于测试包含中间名的姓名。为此,在NamesTestCase类中再添加一个方法:

def test_first_last_middle_name(self):
        """Do names like 'Wolfgang Amadeus Mozart' work?"""
        formatted_name = get_formatted_name(
            'wolfgang', 'mozart', 'amadeus')
        self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart')

再次运行test_name_function.py时,两个测试都通过了:

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
[Finished in 315ms]

动手试一试

练习11-1:城市和国家 编写一个函数,它接受两个形参:一个城市名和一个国家名。这个函数返回一个格式为City, Country的字符串,如Santiago,Chile。将这个函数存储在一个名为city_functions.py的模块中。创建一个名为test_cities.py的程序,对刚才编写的函数进行测试(别忘了,需要导入模块unittest和要测试的函数)。编写一个名为test_city_country()的方法,核实使用类似于'santiago'和'chile'这样的值来调用前述函数时,得到的字符串是正确的。运行test_cities.py,确认测试test_city_country()通过了。

练习11-2:人口数量 修改前面的函数,加上第三个必不可少的形参population,并返回一个格式为City, Country – population xxx的字符串,如Santiago, Chile – population 5000000。运行test_cities.py,确认测试test_city_country()未通过。修改上述函数,将形参population设置为可选的。再次运行test_cities.py,确认测试test_city_country()又通过了。再编写一个名为test_city_country_population()的测试,核实可以使用类似于'santiago'、'chile'和'population=5000000'这样的值来调用这个函数。再次运行test_cities.py,确认测试test_city_country_population()通过了。

11.2 测试类

11.2.1 各种断言方法

6个常用的断言方法:

方法用途
assertEqual(a,b)核实a、b是否相等
assertNotEqual(a,b)核实a、b是否不等
assertTrue(x)核实x是否为真
assertFalse(x)核实x是否为假
assertIn(item,list)核实item是否在list中
assertNotIn(item,list)核实item是否不在list中

11.2.2 一个要测试的类

创建一个类survey.py:帮助管理匿名调查

class AnonymousSurvey:
    """Collect anonymous answers to a survey question."""
    
    def __init__(self, question):
        """Store a question, and prepare to store responses."""
        self.question = question #存储了一个调查问题
        self.responses = []      #用于存储答案
        
    def show_question(self):
        """Show the survey question."""
        print(self.question)    #打印调查问题
        
    def store_response(self, new_response):
        """Store a single response to the survey."""
        self.responses.append(new_response)    #添加新答案
        
    def show_results(self):
        """Show all the responses that have been given."""
        print("Survey results:")
        for response in self.responses: #将存储在列表中的答案都打印出来
            print(f"- {response}")

为证明AnonymousSurvey类能够正确工作,编写一个使用它的程序:language_survey.py

from survey import AnonymousSurvey

# Define a question, and make a survey.
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question) #创建了一个AnonymousSurvey对象

# Show the question, and store responses to the question.
my_survey.show_question()
print("Enter 'q' at any time to quit.\n")
while True:
    response = input("Language: ")
    if response == 'q':
        break
    my_survey.store_response(response)

# Show the survey results.
print("\nThank you to everyone who participated in the survey!")
my_survey.show_results()

?在模块survey中,我们还想改进,可能影响AnonymousSurvey类的当前行为,要确认在开发这个模块时没有破坏既有行为,可以编写针对这个类的测试。

11.2.3 测试AnonymousSurvey类

下面来编写一个测试,对AnonymousSurvey类的行为的一个方面进行验证:如果用户面对调查问题只提供一个答案,这个答案也能被妥善地存储。为此,我们将在这个答案被存储后,使用方法assertIn()来核实它确实在答案列表中:test_survey.py

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase): #测试用例命名为TestAnonymousSurvey
    """Tests for the class AnonymousSurvey"""

    def test_store_single_response(self):
        """Test that a single response is stored properly."""
        question = "What language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        my_survey.store_response("English")
        self.assertIn('English', my_survey.responses)

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

下面来核实当用户提供三个答案时,它们也将被妥善地存储。为此,在TestAnonymousSurvey中再添加一个方法:

    def test_store_three_responses(self):
        """Test that three individual responses are stored properly."""
        question = "What language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        responses=['English','Spanish','Mandarin']
        for response in responses:
            my_survey.store_response(response)
        for response in responses:
            self.assertIn(response, my_survey.responses)

?但这些测试有些重复的地方。下面使用unittest的另一项功能来提高其效率。

11.2.4 方法setUp()

如果在TestCase类中包含了方法setUp(),Python将先运行它,再运行各个以test_打头的方法。

下面使用setUp()来创建一个调查对象和一组答案,供方法test_store_single_response()和test_store_three_responses()使用:

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    """Tests for the class AnonymousSurvey"""
    
    def setUp(self):
        """
        Create a survey and a set of responses for use in all test methods.
        """
        question = "What language did you first learn to speak?"
        self.my_survey = AnonymousSurvey(question)
        self.responses = ['English', 'Spanish', 'Mandarin']

    def test_store_single_response(self):
        """Test that a single response is stored properly."""
        self.my_survey.store_response(self.responses[0])
        self.assertIn(self.responses[0], self.my_survey.responses)

    def test_store_three_responses(self):
        """Test that three individual responses are stored properly."""
        for response in self.responses:
            self.my_survey.store_response(response)
        for response in self.responses:
            self.assertIn(response, self.my_survey.responses)

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

注意 运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试通过时打印一个句点,测试引发错误时打印一个E,而测试导致断言失败时则打印一个F。这就是你运行测试用例时,在输出的第一行中看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试,需要运行很长时间,就可通过观察这些结果来获悉有多少个测试通过了。?

动手试一试

练习11-3:雇员 编写一个名为Employee的类,其方法__init__()接受名、姓和年薪,并将它们存储在属性中。编写一个名为give_raise()的方法,它默认将年薪增加5000美元,但也能够接受其他的年薪增加量。为Employee编写一个测试用例,其中包含两个测试方法:test_give_default_raise()和test_give_custom_raise()。使用方法setUp(),以免在每个测试方法中都新建雇员实例。运行这个测试用例,确认两个测试都通过了。

11.3 小结

在本章中,你学习了:如何使用模块unittest中的工具来为函数和类编写测试;如何编写继承unittest.TestCase的类,以及如何编写测试方法,以核实函数和类的行为符合预期;如何使用方法setUp()来根据类高效地创建实例并设置其属性,以便在类的所有测试方法中使用。

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章           查看所有文章
加:2022-07-17 16:54:38  更:2022-07-17 16:55:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 22:39:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码