mirror of
https://github.com/zulip/zulip.git
synced 2026-06-15 21:01:31 +08:00
This guide was originally written as part of the process of adding i18n support to Zulip, so it had way too much focus on development tooling details not relevant to translators. There's probably a separate follow-up project we should do to move the developers-only content to a separate article, since it's kinda lost here for that audience. Thanks to David Wood for asking a question that inspired me to do this.
281 lines
11 KiB
Markdown
281 lines
11 KiB
Markdown
# Testing and writing tests
|
|
|
|
## Overview
|
|
|
|
Zulip has a full test suite that includes many components. The most
|
|
important components are documented in depth in their own sections:
|
|
|
|
- [Django](../testing/testing-with-django.html): backend Python tests
|
|
- [Casper](../testing/testing-with-casper.html): end-to-end UI tests
|
|
- [Node](../testing/testing-with-node.html): unit tests for JS front end code
|
|
- [Linters](../testing/linters.html): Our parallel linter suite
|
|
- [CI details](continuous-integration.html): How all of these run in CI
|
|
- [Other test suites](#other-test-suites): Our various smaller test suites.
|
|
|
|
This document covers more general testing issues, such as how to run the
|
|
entire test suite, how to troubleshoot database issues, how to manually
|
|
test the front end, etc.
|
|
|
|
We also document [how to manually test the app](manual-testing.html).
|
|
|
|
## Running tests
|
|
|
|
Zulip tests must be run inside a Zulip development environment; if
|
|
you're using Vagrant, you will need to enter the Vagrant environment
|
|
before running the tests:
|
|
|
|
```
|
|
vagrant ssh
|
|
cd /srv/zulip
|
|
```
|
|
|
|
Then, to run the full Zulip test suite, do this:
|
|
```
|
|
./tools/test-all
|
|
```
|
|
|
|
This runs the linter (`tools/lint`) plus all of our test suites;
|
|
they can all be run separately (just read `tools/test-all` to see
|
|
them). You can also run individual tests which can save you a lot of
|
|
time debugging a test failure, e.g.:
|
|
|
|
```
|
|
./tools/lint # Runs all the linters in parallel
|
|
./tools/test-backend zerver.tests.test_bugdown.BugdownTest.test_inline_youtube
|
|
./tools/test-backend BugdownTest # Run `test-backend --help` for more options
|
|
./tools/test-js-with-casper 09-navigation.js
|
|
./tools/test-js-with-node utils.js
|
|
```
|
|
The above setup instructions include the first-time setup of test
|
|
databases, but you may need to rebuild the test database occasionally
|
|
if you're working on new database migrations. To do this, run:
|
|
|
|
```
|
|
./tools/do-destroy-rebuild-test-database
|
|
```
|
|
|
|
## Other test suites
|
|
|
|
Zulip also has about a dozen smaller tests suites:
|
|
|
|
- `tools/test-migrations`: Checks whether the `zerver/migrations`
|
|
migration content the models defined in `zerver/models.py`. See our
|
|
[schema migration documentation](../subsystems/schema-migrations.html)
|
|
for details on how to do database migrations correctly.
|
|
- `tools/test-documentation`: Checks for broken links in this
|
|
ReadTheDocs documentation site.
|
|
- `tools/test-help-documentation`: Checks for broken links in the
|
|
`/help` user documentation site, and related pages.
|
|
- `tools/test-api`: Tests that the API documentation at `/api`
|
|
actually works; the actual code for this is defined in
|
|
`zerver/lib/api_test_helpers.py`.
|
|
- `test-locked-requirements`: Verifies that developers didn't forget
|
|
to run `tools/update-locked-requirements` after modifying
|
|
`requirements/*.in`. See
|
|
[our dependency documentation](../subsystems/dependencies.html) for
|
|
details on the system this is verifying.
|
|
- `tools/check-capitalization`: Checks whether translated strings (aka
|
|
user-facing strings) correctly follow Zulip's capitalization
|
|
conventions. This requires some maintainance of an exclude list
|
|
(`tools.lib.capitalization.IGNORED_PHRASES`) of proper nouns
|
|
mentioned in the Zulip project, but helps a lot in avoiding new
|
|
strings being added that don't match our style.
|
|
- `tools/check-frontend-i18n`: Checks for a common bug in Handlebars
|
|
templates, of using the wrong syntax for translating blocks
|
|
containing variables.
|
|
- `./tools/test-run-dev`: Checks that `run-dev.py` starts properly;
|
|
this helps prevent bugs that break the development environment.
|
|
- `./tools/test-queue-worker-reload`: Verifies that Zulip's queue
|
|
processors properly reload themselves after code changes.
|
|
- `./tools/setup/optimize-svg`: Checks whether all integration logo SVG
|
|
graphics are optimized.
|
|
logos are properly optimized for size (since we're not going to edit
|
|
third-party logos, this helps keep the Zulip codebase from getting huge).
|
|
- `./tools/test-tools`: Automated tests for various parts of our
|
|
development tooling (mostly various linters) that are not used in
|
|
production.
|
|
|
|
Each of these has a reason (usually, performance or a need to do messy
|
|
things to the environment) why they are not part of the handful of
|
|
major test suites like `test-backend`, but they all contribute
|
|
something valuable to helping keep Zulip bug-free.
|
|
|
|
### Possible testing issues
|
|
|
|
- When running the test suite, if you get an error like this:
|
|
|
|
```
|
|
sqlalchemy.exc.ProgrammingError: (ProgrammingError) function ts_match_locs_array(unknown, text, tsquery) does not exist
|
|
LINE 2: ...ECT message_id, flags, subject, rendered_content, ts_match_l...
|
|
^
|
|
```
|
|
|
|
… then you need to install tsearch-extras, described
|
|
above. Afterwards, re-run the `init*-db` and the
|
|
`do-destroy-rebuild*-database` scripts.
|
|
|
|
- Or, when running the test suite, if you get an error involving Git that looks like this:
|
|
|
|
```
|
|
commit_messages| An error occurred while executing '/usr/bin/git rev-list --max-count=-1 upstream/master..HEAD': b"fatal: ambiguous argument 'upstream/master..HEAD': unknown revision or path not in the working tree.\nUse '--' to separate paths from revisions, like this:\n'git <command> [<revision>...] -- [<file>...]'"
|
|
```
|
|
|
|
... then you may need to connect the Zulip upstream repository with the following command:
|
|
|
|
```
|
|
git remote add -f upstream https://github.com/zulip/zulip.git
|
|
```
|
|
|
|
- When building the development environment using Vagrant and the LXC
|
|
provider, if you encounter permissions errors, you may need to
|
|
`chown -R 1000:$(id -g) /path/to/zulip` on the host before running
|
|
`vagrant up` in order to ensure that the synced directory has the
|
|
correct owner during provision. This issue will arise if you run `id
|
|
username` on the host where `username` is the user running Vagrant
|
|
and the output is anything but 1000.
|
|
This seems to be caused by Vagrant behavior; for more information,
|
|
see [the vagrant-lxc FAQ entry about shared folder permissions][lxc-sf].
|
|
|
|
[lxc-sf]: https://github.com/fgrehm/vagrant-lxc/wiki/FAQ#help-my-shared-folders-have-the-wrong-owner
|
|
|
|
- When running casper tests (`./tools/test-js-with-casper`), if you
|
|
get an error like this:
|
|
|
|
```
|
|
Running node_modules/.bin/casperjs test /srv/zulip/frontend_tests/casper_tests/00-realm-creation.js
|
|
internal/child_process.js:289
|
|
var err = this._handle.spawn(options);
|
|
^
|
|
|
|
TypeError: Bad argument
|
|
```
|
|
... it means that phantomjs is not installed. You can install it by running
|
|
the following commands.
|
|
|
|
```bash
|
|
cd node_modules/phantomjs-prebuilt
|
|
node install.js
|
|
```
|
|
|
|
### Internet access inside test suites
|
|
|
|
As a policy matter, the Zulip test suites should never make outgoing
|
|
HTTP or other network requests. This is important for 2 major
|
|
reasons:
|
|
|
|
* Tests that make outgoing Internet requests will fail when the user
|
|
isn't on the Internet.
|
|
* Tests that make outgoing Internet requests often have a hidden
|
|
dependency on the uptime of a third-party service, and will fail
|
|
nondeterministically if that service has a temporary outage.
|
|
Nondeterministically failing tests can be a big waste of
|
|
developer time, and we try to avoid them wherever possible.
|
|
|
|
As a result, Zulip's major test suites should never access the
|
|
Internet directly. Since code in Zulip does need to access the
|
|
Internet (e.g. to access various third-party APIs), this means that
|
|
the Zulip tests use mocking to basically hardcode (for the purposes of
|
|
the test) what responses should be used for any outgoing Internet
|
|
requests that Zulip would make in the code path being tested.
|
|
|
|
This is easy to do using test fixtures (a fancy word for fixed data
|
|
used in tests) and the `mock.patch` function to specify what HTTP
|
|
response should be used by the tests for every outgoing HTTP (or other
|
|
network) request. Consult
|
|
[our guide on mocking](../testing/testing-with-django.html#zulip-mocking-practices) to
|
|
learn how to mock network requests easily; there are also a number of
|
|
examples throughout the codebase.
|
|
|
|
We partially enforce this policy in the main Django/backend test suite
|
|
by overriding certain library functions that are used in outgoing HTTP
|
|
code paths (`httplib2.Http().request`, `requests.request`, etc.) to
|
|
throw an exception in the backend tests. While this is enforcement is
|
|
not complete (there a lot of other ways to use the Internet from
|
|
Python), it is easy to do and catches most common cases of new code
|
|
dependning on Internet access.
|
|
|
|
This enforcement code results in the following exception:
|
|
|
|
```
|
|
File "tools/test-backend", line 120, in internet_guard
|
|
raise Exception("Outgoing network requests are not allowed in the Zulip tests."
|
|
Exception: Outgoing network requests are not allowed in the Zulip tests.
|
|
...
|
|
```
|
|
|
|
#### Documentation tests
|
|
|
|
The one exception to this policy is our documentation tests, which
|
|
will attempt to verify that the links included in our documentation
|
|
aren't broken. Those tests end up failing nondeterministically fairly
|
|
often, which is unfortunate, but there's simply no other correct way
|
|
to verify links other than attempting to access them.
|
|
|
|
## Schema and initial data changes
|
|
|
|
If you change the database schema or change the initial test data, you
|
|
have to regenerate the pristine test database by running
|
|
`tools/do-destroy-rebuild-test-database`.
|
|
|
|
## Wiping the test databases
|
|
|
|
You should first try running: `tools/do-destroy-rebuild-test-database`
|
|
|
|
If that fails you should try to do:
|
|
|
|
sudo -u postgres psql
|
|
> DROP DATABASE zulip_test;
|
|
> DROP DATABASE zulip_test_template;
|
|
|
|
and then run `tools/do-destroy-rebuild-test-database`
|
|
|
|
### Recreating the postgres cluster
|
|
|
|
> **warning**
|
|
>
|
|
> **This is irreversible, so do it with care, and never do this anywhere
|
|
> in production.**
|
|
|
|
If your postgres cluster (collection of databases) gets totally trashed
|
|
permissions-wise, and you can't otherwise repair it, you can recreate
|
|
it. On Ubuntu:
|
|
|
|
sudo pg_dropcluster --stop 9.1 main
|
|
sudo pg_createcluster --locale=en_US.utf8 --start 9.1 main
|
|
|
|
## Local browser testing (local app + web browser)
|
|
|
|
This section is about troubleshooting your local development environment.
|
|
|
|
There is a [separate manual testing doc](manual-testing.html) that
|
|
enumerates things you can test as part of manual QA.
|
|
|
|
### Clearing the development database
|
|
|
|
You can use:
|
|
|
|
./tools/do-destroy-rebuild-database
|
|
|
|
to drop the database on your development environment and repopulate
|
|
your it with the Shakespeare characters and some test messages between
|
|
them. This is run automatically as part of the development
|
|
environment setup process, but is occasionally useful when you want to
|
|
return to a clean state for testing.
|
|
|
|
### JavaScript manual testing
|
|
|
|
`debug.js` has some tools for profiling JavaScript code, including:
|
|
|
|
- \`print\_elapsed\_time\`: Wrap a function with it to print the time
|
|
that function takes to the JavaScript console.
|
|
- \`IterationProfiler\`: Profile part of looping constructs (like a
|
|
for loop or \$.each). You mark sections of the iteration body and
|
|
the IterationProfiler will sum the costs of those sections over all
|
|
iterations.
|
|
|
|
Chrome has a very good debugger and inspector in its developer tools.
|
|
Firebug for Firefox is also pretty good. They both have profilers, but
|
|
Chrome's is a sampling profiler while Firebug's is an instrumenting
|
|
profiler. Using them both can be helpful because they provide different
|
|
information.
|