mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	docs: Improve documentation on node test suite.
The most important part of this is encouraging folks to read the zjquery documentation.
This commit is contained in:
		@@ -1,7 +1,10 @@
 | 
			
		||||
# JavaScript unit tests
 | 
			
		||||
 | 
			
		||||
As an alternative to the black-box whole-app testing, you can unit test
 | 
			
		||||
individual JavaScript files.
 | 
			
		||||
Our node-based JavaScript unit tests system is the preferred way to
 | 
			
		||||
test JavaScript code in Zulip.  We prefer it over the
 | 
			
		||||
[Casper black-box whole-app testing](../testing/testing-with-casper.html),
 | 
			
		||||
system since it is much (>100x) faster and also easier to do correctly
 | 
			
		||||
than the Casper system.
 | 
			
		||||
 | 
			
		||||
You can run tests as follow:
 | 
			
		||||
```
 | 
			
		||||
@@ -36,32 +39,46 @@ see if there are corresponding test in `frontend_tests/node_tests`.  If
 | 
			
		||||
there are, you should strive to follow the patterns of the existing tests
 | 
			
		||||
and add your own tests.
 | 
			
		||||
 | 
			
		||||
## Coverage reports
 | 
			
		||||
## How the node tests work
 | 
			
		||||
 | 
			
		||||
You can automatically generate coverage reports for the JavaScript unit
 | 
			
		||||
tests like this:
 | 
			
		||||
Unlike the [casper unit tests](../testing/testing-with-casper.html),
 | 
			
		||||
which use the `phantomjs` browser connected to a running Zulip
 | 
			
		||||
deveopment server, our node unit tests don't have a browser, don't
 | 
			
		||||
talk to a server, and generally don't use a complete virtual DOM (a
 | 
			
		||||
handful of tests use the `jsdom` library for this purpose) because
 | 
			
		||||
those slow down the tests a lot, and often don't add much value.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
    tools/test-js-with-node --coverage
 | 
			
		||||
```
 | 
			
		||||
Instead, the preferred model for our unit tests is to mock DOM
 | 
			
		||||
manipulations (which in Zulip are almost exclusively done via
 | 
			
		||||
`jQuery`) using a custom library
 | 
			
		||||
[zjquery](https://github.com/zulip/zulip/blob/master/frontend_tests/zjsunit/zjquery.js).
 | 
			
		||||
 | 
			
		||||
If tests pass, you will get instructions to view coverage reports
 | 
			
		||||
in your browser.
 | 
			
		||||
The
 | 
			
		||||
[unit test file](https://github.com/zulip/zulip/blob/master/frontend_tests/node_tests/zjquery.js)
 | 
			
		||||
for `zjquery` is designed to be also serve as nice documentation for
 | 
			
		||||
how to use `zjquery`, and is **highly recommended reading** for anyone
 | 
			
		||||
working on or debugging the Zulip node tests.
 | 
			
		||||
 | 
			
		||||
Note that modules that
 | 
			
		||||
we don't test *at all* aren't listed in the report, so this tends to
 | 
			
		||||
overstate how good our overall coverage is, but it's accurate for
 | 
			
		||||
individual files. You can also click a filename to see the specific
 | 
			
		||||
statements and branches not tested. 100% branch coverage isn't
 | 
			
		||||
necessarily possible, but getting to at least 80% branch coverage is a
 | 
			
		||||
good goal.
 | 
			
		||||
Conceptually, the `zjquery` library provides minimal versions of most
 | 
			
		||||
`jQuery` DOM manipulation functions, and has a convenient system for
 | 
			
		||||
letting you setup return values for more complex functions.  For
 | 
			
		||||
example, if the code you'd like to test calls `$obj.find()`, you can
 | 
			
		||||
use `$obj.set_find_results(selector, $value)` to setup `zjquery` so
 | 
			
		||||
that calls to `$obj.find(selector)` will return `$value`.  See the
 | 
			
		||||
unit test file for details.
 | 
			
		||||
 | 
			
		||||
The `zjquery` library itself is only about 500 lines of code, and can
 | 
			
		||||
also be a useful resource if you're having trouble debugging DOM
 | 
			
		||||
access in the unit tests.
 | 
			
		||||
 | 
			
		||||
## Handling dependencies in unit tests
 | 
			
		||||
 | 
			
		||||
The following scheme helps avoid tests leaking globals between each
 | 
			
		||||
other.
 | 
			
		||||
 | 
			
		||||
You want to categorize each module as follows:
 | 
			
		||||
The other big challenge with doing unit tests for a JavaScript project
 | 
			
		||||
is that often one wants to limit the scope the production code being
 | 
			
		||||
run, just to avoid doing extra setup work that isn't relevant to the
 | 
			
		||||
code you're trying to test.  For that reason, each unit test file
 | 
			
		||||
explicitly declares all of the modules it depends on, with a few
 | 
			
		||||
different types of declarations depending on whether we want to:
 | 
			
		||||
 | 
			
		||||
-   Exercise the module's real code for deeper, more realistic testing?
 | 
			
		||||
-   Stub out the module's interface for more control, speed, and
 | 
			
		||||
@@ -75,8 +92,8 @@ like the following toward the top of your test file:
 | 
			
		||||
>     zrequire('stream_data');
 | 
			
		||||
>     zrequire('Filter', 'js/filter');
 | 
			
		||||
 | 
			
		||||
For modules that you want to completely stub out, please use a pattern
 | 
			
		||||
like this:
 | 
			
		||||
For modules that you want to completely stub out, use a pattern like
 | 
			
		||||
this:
 | 
			
		||||
 | 
			
		||||
>     set_global('page_params', {
 | 
			
		||||
>         email: 'bob@zulip.com'
 | 
			
		||||
@@ -85,17 +102,20 @@ like this:
 | 
			
		||||
>     // then maybe further down
 | 
			
		||||
>     page_params.email = 'alice@zulip.com';
 | 
			
		||||
 | 
			
		||||
Finally, there's the hybrid situation, where you want to borrow some of
 | 
			
		||||
a module's real functionality but stub out other pieces. Obviously, this
 | 
			
		||||
is a pretty strong smell that the other module might be lacking in
 | 
			
		||||
cohesion, but that code might be outside your jurisdiction. The pattern
 | 
			
		||||
here is this:
 | 
			
		||||
One can similarly stub out functions in a module's exported interface
 | 
			
		||||
with either `noop` functions or actual code.
 | 
			
		||||
 | 
			
		||||
Finally, there's the hybrid situation, where you want to borrow some
 | 
			
		||||
of a module's real functionality but stub out other pieces. Obviously,
 | 
			
		||||
this is a pretty strong code smell that the other module might be
 | 
			
		||||
lacking in cohesion, but sometimes it's not worth going down the
 | 
			
		||||
rabbit hole of trying to improve that. The pattern here is this:
 | 
			
		||||
 | 
			
		||||
>     // Import real code.
 | 
			
		||||
>     zrequire('narrow');
 | 
			
		||||
>     zrequire('narrow_state');
 | 
			
		||||
>
 | 
			
		||||
>     // And later...
 | 
			
		||||
>     narrow.stream = function () {
 | 
			
		||||
>     narrow_state.stream = function () {
 | 
			
		||||
>         return 'office';
 | 
			
		||||
>     };
 | 
			
		||||
 | 
			
		||||
@@ -116,3 +136,24 @@ is at the bottom of `foobar.js`:
 | 
			
		||||
This means `foobar.js` follow the CommonJS module pattern, so it can be
 | 
			
		||||
required in Node.js, which runs our tests.
 | 
			
		||||
 | 
			
		||||
## Coverage reports
 | 
			
		||||
 | 
			
		||||
You can automatically generate coverage reports for the JavaScript unit
 | 
			
		||||
tests like this:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
    tools/test-js-with-node --coverage
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If tests pass, you will get instructions to view coverage reports
 | 
			
		||||
in your browser.
 | 
			
		||||
 | 
			
		||||
Note that modules that we don't test *at all* aren't listed in the
 | 
			
		||||
report, so this tends to overstate how good our overall coverage is,
 | 
			
		||||
but it's accurate for individual files. You can also click a filename
 | 
			
		||||
to see the specific statements and branches not tested. 100% branch
 | 
			
		||||
coverage isn't necessarily possible, but getting to at least 80%
 | 
			
		||||
branch coverage is a good goal.
 | 
			
		||||
 | 
			
		||||
The overall project goal is to get to 100% node test coverage on all
 | 
			
		||||
data/logic modules (UI modules are lower priority for unit testing).
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user