testing.md 7.56 KB
Newer Older
1 2
# Testing

3
## Overview
4

5 6
We maintain three kinds of tests: unit tests, integration tests,
and acceptance tests.
7

8
### Unit Tests
9

10 11 12
* Each test case should be concise: setup, execute, check, and teardown.
If you find yourself writing tests with many steps, consider refactoring 
the unit under tests into smaller units, and then testing those individually.
13

Will Daly committed
14 15
* As a rule of thumb, your unit tests should cover every code branch.

16 17
* Mock or patch external dependencies.
We use [voidspace mock](http://www.voidspace.org.uk/python/mock/).
Jay Zoldak committed
18

19 20
* We unit test Python code (using [unittest](http://docs.python.org/2/library/unittest.html)) and 
Javascript (using [Jasmine](http://pivotal.github.io/jasmine/))
Jay Zoldak committed
21

22 23 24 25 26 27
### Integration Tests
* Test several units at the same time.
Note that you can still mock or patch dependencies
that are not under test!  For example, you might test that 
`LoncapaProblem`, `NumericalResponse`, and `CorrectMap` in the 
`capa` package work together, while still mocking out template rendering.
Jay Zoldak committed
28

29 30 31 32
* Use integration tests to ensure that units are hooked up correctly.
You do not need to test every possible input--that's what unit 
tests are for.  Instead, focus on testing the "happy path" 
to verify that the components work together correctly.
33

34 35
* Many of our tests use the [Django test client](https://docs.djangoproject.com/en/dev/topics/testing/overview/) to simulate
HTTP requests to the server.
36

37 38 39 40 41 42 43 44 45 46 47 48
### UI Acceptance Tests
* Use these to test that major program features are working correctly.

* We use [lettuce](http://lettuce.it/) to write BDD-style tests.  Most of
these tests simulate user interactions through the browser using
[splinter](http://splinter.cobrateam.info/).

Overall, you want to write the tests that **maximize coverage**
while **minimizing maintenance**.
In practice, this usually means investing heavily 
in unit tests, which tend to be the most robust to changes in the code base.  

49 50 51 52
![Test Pyramid](test_pyramid.png)

The pyramid above shows the relative number of unit tests, integration tests,
and acceptance tests.  Most of our tests are unit tests or integration tests.
53 54 55 56 57 58 59 60 61 62 63 64

## Test Locations

* Python unit and integration tests: Located in 
subpackages called `tests`.
For example, the tests for the `capa` package are located in 
`common/lib/capa/capa/tests`.

* Javascript unit tests: Located in `spec` folders.  For example,
`common/lib/xmodule/xmodule/js/spec` and `{cms,lms}/static/coffee/spec`  
For consistency, you should use the same directory structure for implementation
and test.  For example, the test for `src/views/module.coffee`
65 66
should be written in `spec/views/module_spec.coffee`.

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
* UI acceptance tests:
    - Set up and helper methods: `common/djangoapps/terrain`
    - Tests: located in `features` subpackage within a Django app.
    For example: `lms/djangoapps/courseware/features`


## Factories

Many tests delegate set-up to a "factory" class.  For example,
there are factories for creating courses, problems, and users.
This encapsulates set-up logic from tests.

Factories are often implemented using [FactoryBoy](https://readthedocs.org/projects/factoryboy/)

In general, factories should be located close to the code they use.
For example, the factory for creating problem XML definitions
 is located in `common/lib/capa/capa/tests/response_xml_factory.py`
because the `capa` package handles problem XML.


# Running Tests

Before running tests, ensure that you have all the dependencies.  You can install dependencies using:

Will Daly committed
91
    pip install -r requirements.txt
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127


## Running Python Unit tests

We use [nose](https://nose.readthedocs.org/en/latest/) through
the [django-nose plugin](https://pypi.python.org/pypi/django-nose)
to run the test suite.

You can run tests using `rake` commands.  For example,

    rake test

runs all the tests.  It also runs `collectstatic`, which prepares the static files used by the site (for example, compiling Coffeescript to Javascript).  

You can also run the tests without `collectstatic`, which tends to be faster:

    rake fasttest_lms

or

    rake fasttest_cms

xmodule can be tested independently, with this:

    rake test_common/lib/xmodule

To run a single django test class:

    django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/courseware/tests/tests.py:TestViewAuth

To run a single django test:

    django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/courseware/tests/tests.py:TestViewAuth.test_dark_launch


To run a single nose test file:
128

129
    nosetests common/lib/xmodule/xmodule/tests/test_stringify.py
Jay Zoldak committed
130

131
To run a single nose test:
Jay Zoldak committed
132

133
    nosetests common/lib/xmodule/xmodule/tests/test_stringify.py:test_stringify
Jay Zoldak committed
134 135


136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
Very handy: if you uncomment the `pdb=1` line in `setup.cfg`, it will drop you into pdb on error.  This lets you go up and down the stack and see what the values of the variables are.  Check out [the pdb documentation](http://docs.python.org/library/pdb.html)

### Running Javascript Unit Tests

These commands start a development server with jasmine testing enabled, and launch your default browser
pointing to those tests

    rake browse_jasmine_{lms,cms}

To run the tests headless, you must install [phantomjs](http://phantomjs.org/download.html), then run:

    rake phantomjs_jasmine_{lms,cms}

If the `phantomjs` binary is not on the path, set the `PHANTOMJS_PATH` environment variable to point to it

    PHANTOMJS_PATH=/path/to/phantomjs rake phantomjs_jasmine_{lms,cms}

153 154
Once you have run the `rake` command, your browser should open to 
to `http://localhost/_jasmine/`, which displays the test results.
155

156 157
**Troubleshooting**: If you get an error message while running the `rake` task,
try running `bundle install` to install the required ruby gems.
158 159 160 161 162 163

### Running Acceptance Tests

We use [Lettuce](http://lettuce.it/) for acceptance testing.
Most of our tests use [Splinter](http://splinter.cobrateam.info/)
to simulate UI browser interactions.  Splinter, in turn,
164
uses [Selenium](http://docs.seleniumhq.org/) to control the Chrome browser.
165 166

**Prerequisite**: You must have [ChromeDriver](https://code.google.com/p/selenium/wiki/ChromeDriver) 
167 168 169
installed to run the tests in Chrome.  The tests are confirmed to run
with Chrome (not Chromium) version 26.0.0.1410.63 with ChromeDriver
version r195636.
170

171
To run all the acceptance tests:
172

173 174
    rake test_acceptance_lms
    rake test_acceptance_cms
175

176
To test only a specific feature:
177

178
    rake test_acceptance_lms[lms/djangoapps/courseware/features/problems.feature]
179

180 181 182 183 184 185
To start the debugger on failure, add the `--pdb` option:

    rake test_acceptance_lms["lms/djangoapps/courseware/features/problems.feature --pdb"]

To run tests faster by not collecting static files, you can use
`rake fasttest_acceptance_lms` and `rake fasttest_acceptance_cms`.
186 187 188 189 190


**Troubleshooting**: If you get an error message that says something about harvest not being a command, you probably are missing a requirement.
Try running:

Will Daly committed
191
    pip install -r requirements.txt
192

193
**Note**: The acceptance tests can *not* currently run in parallel.  
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

## Viewing Test Coverage

We currently collect test coverage information for Python unit/integration tests.

To view test coverage:

1. Run the test suite:

        rake test

2. Generate reports:

        rake coverage:html

3. HTML reports are located in the `reports` folder.


## Testing using queue servers

When testing problems that use a queue server on AWS (e.g. sandbox-xqueue.edx.org), you'll need to run your server on your public IP, like so.
Jay Zoldak committed
215

216
`django-admin.py runserver --settings=lms.envs.dev --pythonpath=. 0.0.0.0:8000`
Jay Zoldak committed
217

218
When you connect to the LMS, you need to use the public ip.  Use `ifconfig` to figure out the number, and connect e.g. to `http://18.3.4.5:8000/`