好,坏,邪恶-在初学者项目中进行测试

前言:大学收到了一项任务-组建一支Scrum团队,选择一个项目并在一个学期内进行研究。 我们的团队选择了Web应用程序开发(反应+烧瓶)。 在本文中,我将尝试告诉您应该进行哪些测试,并分析我们在后端进行的测试。



期望值


首先,必须进行测试,才能说服所有人(包括我们自己)该程序在测试情况下的行为。 其次,它们确保将来测试所覆盖的代码的性能。 编写测试是一个有用的过程,因为在此过程中,您经常会偶然发现问题区域,回忆一些极端情况,查看界面问题等。


开发任何系统时,您都需要记住至少三种类型的测试:


  • 单元测试是验证功能是否能够满足其需要的测试。
  • 集成测试是验证多个功能一起执行正确操作的测试。
  • 系统测试是验证整个系统是否能够完成所需工作的测试。

在google 的一篇帖子中 ,发布了一个表格,其中描述了三种测试类型。 “小”,“中”和“大”。



单元测试


单元测试对应于小型测试-单元测试应该快速并且仅检查程序特定部分的正确性。 他们不应该访问数据库,也不应该在复杂的多线程环境中工作。 他们控制对规范/标准的遵守,通常它们具有回归测试的作用。


整合测试


集成测试是那些可能影响多个模块和功能的测试。 这样的测试需要更多的时间,并且可能需要特殊的环境。 它们是确保各个模块和功能可以相互配合使用的必要条件。 即 单元测试验证了真实接口与预期接口以及集成测试的一致性,即功能和模块之间可以正确交互。


系统测试


这是自动测试的最高级别。 系统测试可验证整个系统是否正常运行,其各个部分执行任务并能够正确交互。


为什么要跟踪类型


通常,随着项目的增长,代码库也会增长。 自动检查的持续时间将增加;支持大量集成和系统测试将变得越来越困难。 因此,开发人员面临的挑战是最小化必要的测试。 为此,请尝试尽可能使用单元测试,并使用“模拟”(模拟)减少集成。


现实性


典型的API测试


def test_user_reg(client): return json.loads( client.post(url, json=data, content_type='application/json').data ) response = client.post('api/user.reg', json={ 'email': 'name@mail.ru', 'password': 'password1', 'first_name': 'Name', 'last_name': 'Last Name' }) data = json.loads(response.data) assert data['code'] == 0 

flask官方文档中,我们获得了现成的配方,用于初始化应用程序和创建数据库。 这是数据库的工作。 这不是单元测试,也不是系统测试。 这是使用数据库测试应用程序的集成测试。


为什么要集成而不是模块化? 因为在查询处理中,交互是通过flask,ORM和我们的业务逻辑来执行的。 处理程序是项目其他部分的统一元素,因此为它们编写单元测试不是很容易(您需要用模拟,内部逻辑替换数据库),也不是太实用(集成测试将检查类似的方面-“是否调用了必要的功能?”,“是否正确接收了数据?”等)。


测试的名称和分组


 def test_not_empty_errors(): assert validate_not_empty('email', '') == ('email is empty',) assert validate_not_empty('email', ' ') == ('email is empty',) assert validate_email_format('email', "") == ('email is empty',) assert validate_password_format('pass', "") == ('pass is empty',) assert validate_datetime('datetime', "") == ('datetime is empty',) 

在此测试中,将满足“小型”测试的所有条件-检查没有依赖项的功能的行为是否符合预期。 但是设计提出了问题。


优良作法是编写针对程序特定方面的测试。 在此示例中,存在不同的功能validate_password_formatvalidate_password_formatvalidate_datetime 。 分组检查不是基于结果,而是基于测试对象。


测试的名称( test_not_empty_errors )不描述测试对象(正在测试哪个方法),而仅描述结果(错误不为空)。 此方法应称为test__validate_not_empty__error_on_empty 。 此名称描述了正在测试的内容以及预期的结果。 由于几乎没有时间讨论测试命名约定,因此这几乎适用于项目中的每个测试名称。


回归测试


 def test_datetime_errors(): assert validate_datetime('datetime', '0123-24-31T;431') == ('datetime is invalid',) assert validate_datetime('datetime', '2018-10-18T20:21:21+-23:1') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-13-20T20:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-02-29T20:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T25:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:61:20+22:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:61+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:20+25:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:20+20:61') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-13-35T25:61:61+61:61') == ('datetime is invalid',) 

该测试最初由前两个assert 。 此后,发现了一个“错误”,而不是检查日期,而是仅检查了正则表达式。 9999-99-99被认为是正常日期。 开发人员已修复它。 自然,修复该错误后,您需要添加测试以防止将来出现回归。 代替添加新的测试以写出该测试存在的原因,而是在此测试中添加了检查。


在添加验证的新测试中应该调用什么? 可能是test__validate_datetime__error_on_bad_datetime


忽略工具


 def test_get_providers(): class Tmp: def __init__(self, id_external, token, username): self.id_external = id_external self.token = token self.username = username ... 

Tmp ? 这是该测试中未使用的对象的替代。 开发人员似乎并不知道unittest.mock是否存在@patchMagicMock 。 当有更多合适的工具时,无需通过天真地解决问题来使代码复杂化。


有一个这样的测试可以初始化服务(在数据库中),使用应用程序上下文。


 def test_get_posts(client): def fake_request(*args, **kwargs): return [one, two] handler = VKServiceHandler() handler.request = fake_request services_init() with app.app_context(): posts = handler.get_posts(None) assert len(posts) == 2 

您只需添加一个@patch即可从测试中排除数据库和上下文。


 @patch("mobius.services.service_vk.Service") def test_get_posts(mock): def fake_request(*args, **kwargs): return [one, two] handler = VKServiceHandler() handler.request = fake_request posts = handler.get_posts(None) assert len(posts) == 2 

总结


  • 要开发高质量的软件,您需要编写测试。 至少要确保您写下所需的内容。
  • 对于大型信息系统,测试甚至更为重要-它们使您避免不必要的界面更改或返回错误。
  • 为了使书面测试不会随着时间的流逝而变成许多奇怪的方法,您需要注意测试的命名约定,遵守良好做法,并尽量减少测试。
  • 在开发过程中,单元测试可能是一个很好的工具。 它们可以在每次进行小更改后运行,以确保没有任何损坏。

非常重要的一点是,测试不能保证缺陷的可用性或不存在缺陷。 测试确保程序(或其一部分)的真实结果是预期的。 在这种情况下,仅对编写测试的那些方面进行验证。 因此,在创建高质量产品时,我们不应忘记其他类型的测试。

Source: https://habr.com/ru/post/zh-CN450290/


All Articles