整理自官方开发文档
作为 CI
流程的一部分,我们在 Sentry
运行了多种测试。本节旨在记录一些 sentry
特定的帮助程序, 并提供有关在构建新功能时应考虑包括哪些类型的测试的指南。
验收和 python
测试需要一组有效的 devservices
。建议使用 devservices
来确保所需要的服务正在运行。如果您还使用本地环境进行本地测试,您将需要使用 --project
标志将本地测试卷与测试套件卷分开:
# 关闭本地测试服务。
sentry devservices down
# 打开带有 test 前缀的服务以使用单独的容器和卷
sentry devservices up --project test
# 验证测试容器是否正确出现
docker ps --format '{{.Names}}'
# 稍后当您完成运行测试并想再次运行本地服务器时
sentry devservices down --project test && sentry devservices up
使用 --project
选项时,您可以确认哪些容器正在运行 docker ps
。每个正在运行的容器都应该以 test_
为前缀。有关管理服务的更多信息,请参阅 devservices docs 部分。
对于 python
测试,我们使用 pytest 和 Django
提供的测试工具。在此基础之上,我们添加了一些基本测试用例(在 sentry.testutils.cases
中)。
端点集成测试是我们大部分测试套件的重点所在。这些测试帮助我们确保我们的 customers
、integrations
和前端应用程序的 API
继续以预期的方式工作。您应该努力包含涵盖各种用户角色
、跨组织/团队
访问场景以及无效数据场景的测试,因为这些在手动测试时经常被忽略。
您可以根据更改的范围使用 pytest
运行单个目录
、单个文件
或单个测试
:
# 对整个目录运行测试
pytest tests/sentry/api/endpoints/
# 对目录中匹配模式的所有文件运行测试
pytest tests/sentry/api/endpoints/test_organization_*.py
# 从单个文件运行测试
pytest tests/sentry/api/endpoints/test_organization_group_index.py
# 运行单个测试
pytest tests/snuba/api/endpoints/test_organization_events_distribution.py::OrganizationEventsDistributionEndpointTest::test_this_thing
# 在匹配子字符串的文件中运行所有测试
pytest tests/snuba/api/endpoints/test_organization_events_distribution.py -k method_name
pytest
的一些常用选项是:
-k
通过子字符串过滤测试方法/类
。-s
在运行测试时不要捕获标准输出。有关更多使用选项,请参阅 pytest 文档。
Sentry
还添加了一套 factory
辅助方法,可帮助您构建数据以针对其编写测试。 sentry.testutils.factories
中的工厂方法可用于我们所有的测试套件类。使用这些方法来建立所需的组织
、项目
和其他基于 postgres
的状态。
您还应该使用 store_event()
以类似于应用程序在生产中所做的方式存储事件。存储事件需要您的测试继承自 SnubaTestCase
。使用 store_event()
时,请注意在事件上设置过去的 timestamp
。省略时,timestamp
将使用 'now'
,这可能会导致由于 timestamp
边界而无法选择事件。
from sentry.testutils.helpers.datetime import before_now
from sentry.utils.samples import load_data
def test_query(self):
data = load_data("python", timestamp=before_now(minutes=1))
event = self.store_event(data, project_id=self.project.id)
如果您的测试是针对带有功能标记的端点,或者需要设置特定选项。您可以使用辅助方法将配置数据更改为正确的状态:
def test_success(self):
with self.feature('organization:new-thing'):
with self.options({'option': 'value'}):
# Run test logic with features and options set.
# Disable the new-thing feature.
with self.feature({'organization:new-thing': False}):
# Run you logic with a feature off.
使用 responses
库为您的代码发出的出站 API
请求添加存根响应。这将帮助您相对轻松地模拟成功和失败的场景。
在编写与摄取事件
相关的测试时,我们必须在事件的约束内操作不能超过 30
天。因为所有事件都必须是最近的,所以我们不能使用传统的时间冻结策略
在测试中获得一致的数据。我们不是选择任意的时间点,而是从现在开始向后工作,并且有一些辅助函数可以这样做:
from sentry.testutils.helpers.datetime import before_now, iso_format
five_min_ago = before_now(minutes=5)
iso_timestamp = iso_format(five_min_ago)
这些函数生成 datetime
对象,以及相对于当前的 ISO 8601
格式的 datetime
字符串, 使您能够在已知时间偏移处拥有事件,而不会违反 relay
强加的 30
天限制。
将以下内容添加到项目根目录中的 conftest.py
中:
import itertools
from django.conf import settings
from django.db import connection, connections, reset_queries
from django.template import Template, Context
@pytest.fixture(scope="function", autouse=True)
def log_sql():
reset_queries()
settings.DEBUG = True
yield
time = sum([float(q["time"]) for q in connection.queries])
t = Template(
"{% for sql in sqllog %}{{sql.sql|safe}}{% if not forloop.last %}\n\n{% endif %}{% endfor %}"
)
queries = list(itertools.chain.from_iterable([conn.queries for conn in connections.all()]))
log = t.render(Context({"sqllog": queries, "count": len(queries), "time": time}))
print(log)
现在,在测试期间执行的所有 SQL
都将打印到标准输出。建议使用 pytest
的 -k
选择器缩小输出范围。另请注意,您需要通过 -s
来查看标准输出。
我们的验收测试利用 selenium
和 chromedriver
来模拟用户使用前端应用程序和整个后端堆栈。我们在 Sentry
使用验收测试有两个目的:
Jest
无法涵盖的工作流程。GitHub Actions
为视觉回归测试准备快照。验收测试可以在 tests/acceptance
中找到,并使用 pytest
在本地运行。
当您运行验收测试时,webpack
将自动运行以构建静态资资源。如果您在创建或修改验收测试时更改 Javascript
文件, 则每次更改后都需要 rm .webpack.meta
以触发静态资源的重建。
# 运行单个验收测试。
pytest tests/acceptance/test_organization_group_index.py \
-k test_with_onboarding
# 运行带有头的浏览器,以便您可以观看它。
pytest tests/acceptance/test_organization_group_index.py \
--no-headless=true \
-k test_with_onboarding
# 打开每个 snapshot image
SENTRY_SCREENSHOT=1 VISUAL_SNAPSHOT_ENABLE=1 \
pytest tests/acceptance/test_organization_group_index.py \
-k test_with_onboarding
如果您看到:
WARNING: Failed to gather log types: Message: unknown > command: Cannot call non W3C standard command while in W3C mode
则表示Webpack
未正确编译资源。
因为我们使用 emotion
,所以我们的类名通常对浏览器自动化没有用。相反,我们自由地使用 data-test-id
属性来定义浏览器自动化和 Jest
测试的 hook
点。
我们所有的数据都异步加载到前端,验收测试需要考虑各种延迟和响应时间。我们倾向于使用 selenium
的 wait_until*
特性来轮询 DOM
,直到元素出现或可见。我们不使用 sleep()
。
像素很重要,因此我们使用视觉回归来帮助捕捉 Sentry
渲染方式的意外变化。在验收测试期间,我们捕获屏幕截图并将您的拉取请求中的屏幕截图与批准的基线进行比较。
虽然我们对视觉回归有相当广泛的覆盖,但仍有一些重要的盲点:
Hover
)卡片与悬停状态所有这些组件和交互通常不包含在可视化快照中,您在处理其中任何一个时都应该小心。
因为视觉回归比较图像快照,而且我们数据的很大一部分处理时间序列数据, 所以我们经常需要用 'fixed'
数据替换基于时间的内容。您可以使用 getDynamicText
帮助程序为依赖于当前时间或变化 过于频繁而无法包含在可视快照中的组件/数据
提供固定内容。
我们的 Jest
套件涵盖为前端组件提供功能和单元测试。我们更喜欢编写与组件交互并观察结果(导航、API 调用)的功能测试, 而不是检查 prop
传递和 state
突变。请参阅 Frontend Handbook 了解更多 Jest
测试技巧。
# Run jest in interactive mode
yarn test
# Run a single test
yarn test tests/js/spec/views/issueList/overview.spec.js
因为我们的 Jest
测试在没有 API
的情况下运行, 所以我们有各种 fixture
构建器可用于帮助生成 API
响应有效负载。 TestStubs
全局包括 tests/js/sentry-test/fixtures/
中的所有 fixture
函数。
您还应该使用 MockApiClient.addMockResponse()
来设置您的组件将进行的 API 调用的响应。未能模拟端点将导致测试失败。
Snuba
测试套件 (.github/workflows/snuba-integration-test.yml
) 是唯一真正让 Kafka
在 CI
中运行的测试套件。如果您有一个需要 Kafka
运行的测试,那么这些测试需要嵌套在 Snuba
测试文件夹 (tests/snuba/
) 下。如果不这样做,您的测试将超时并在 GH actions
中被取消。