Commit 91fb9f44 authored by Rémy Coutable's avatar Rémy Coutable

Improve testing documentation with Robert's feedback

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parent 35bf7c7e
...@@ -87,16 +87,16 @@ JavaScript enabled: ...@@ -87,16 +87,16 @@ JavaScript enabled:
```ruby ```ruby
# For one spec # For one spec
it 'presents information about abuse report', :js do it 'presents information about abuse report', :js do
# assertions... # assertions...
end end
describe "Admin::AbuseReports", :js do describe "Admin::AbuseReports", :js do
it 'presents information about abuse report' do it 'presents information about abuse report' do
# assertions... # assertions...
end end
it 'shows buttons for adding to abuse report' do it 'shows buttons for adding to abuse report' do
# assertions... # assertions...
end end
end end
``` ```
...@@ -112,13 +112,12 @@ file for the failing spec, add the `@javascript` flag above the Scenario: ...@@ -112,13 +112,12 @@ file for the failing spec, add the `@javascript` flag above the Scenario:
``` ```
@javascript @javascript
Scenario: Developer can approve merge request Scenario: Developer can approve merge request
Given I am a "Shop" developer Given I am a "Shop" developer
And I visit project "Shop" merge requests page And I visit project "Shop" merge requests page
And merge request 'Bug NS-04' must be approved And merge request 'Bug NS-04' must be approved
And I click link "Bug NS-04" And I click link "Bug NS-04"
When I click link "Approve" When I click link "Approve"
Then I should see approved merge request "Bug NS-04" Then I should see approved merge request "Bug NS-04"
``` ```
[capybara]: http://teamcapybara.github.io/capybara/ [capybara]: http://teamcapybara.github.io/capybara/
......
...@@ -15,7 +15,10 @@ importance. ...@@ -15,7 +15,10 @@ importance.
Formal definition: https://en.wikipedia.org/wiki/Unit_testing Formal definition: https://en.wikipedia.org/wiki/Unit_testing
These kind of tests ensure that a single unit of code (a method) works as expected (given an input, it has a predictable output). These tests should be isolated as much as possible (for instance model methods that don't do anything with the database shouldn't need a DB record). These kind of tests ensure that a single unit of code (a method) works as
expected (given an input, it has a predictable output). These tests should be
isolated as much as possible (for example, model methods that don't do anything
with the database shouldn't need a DB record).
| Code path | Tests path | Testing engine | Notes | | Code path | Tests path | Testing engine | Notes |
| --------- | ---------- | -------------- | ----- | | --------- | ---------- | -------------- | ----- |
...@@ -42,6 +45,7 @@ These kind of tests ensure that individual parts of the application work well to ...@@ -42,6 +45,7 @@ These kind of tests ensure that individual parts of the application work well to
| Code path | Tests path | Testing engine | Notes | | Code path | Tests path | Testing engine | Notes |
| --------- | ---------- | -------------- | ----- | | --------- | ---------- | -------------- | ----- |
| `app/controllers/` | `spec/controllers/` | RSpec | | | `app/controllers/` | `spec/controllers/` | RSpec | |
| `app/mailers/` | `spec/mailers/` | RSpec | |
| `lib/api/` | `spec/requests/api/` | RSpec | | | `lib/api/` | `spec/requests/api/` | RSpec | |
| `lib/ci/api/` | `spec/requests/ci/api/` | RSpec | | | `lib/ci/api/` | `spec/requests/ci/api/` | RSpec | |
| `app/assets/javascripts/` | `spec/javascripts/` | Karma | More details in the [JavaScript](#javascript) section. | | `app/assets/javascripts/` | `spec/javascripts/` | Karma | More details in the [JavaScript](#javascript) section. |
...@@ -49,9 +53,9 @@ These kind of tests ensure that individual parts of the application work well to ...@@ -49,9 +53,9 @@ These kind of tests ensure that individual parts of the application work well to
#### About controller tests #### About controller tests
In an ideal world, controllers should be thin. However, when this is not the In an ideal world, controllers should be thin. However, when this is not the
case, it's acceptable to write a system test without JavaScript instead of a case, it's acceptable to write a system/feature test without JavaScript instead
controller test. The reason is that testing a fat controller usually involves a of a controller test. The reason is that testing a fat controller usually
lot of stubbing, things like: involves a lot of stubbing, things like:
```ruby ```ruby
controller.instance_variable_set(:@user, user) controller.instance_variable_set(:@user, user)
...@@ -90,12 +94,15 @@ possible). ...@@ -90,12 +94,15 @@ possible).
[Poltergeist]: https://github.com/teamcapybara/capybara#poltergeist [Poltergeist]: https://github.com/teamcapybara/capybara#poltergeist
[RackTest]: https://github.com/teamcapybara/capybara#racktest [RackTest]: https://github.com/teamcapybara/capybara#racktest
#### Good practices #### Best practices
- Create only the necessary records in the database - Create only the necessary records in the database
- Test a happy path and a less happy path but that's it - Test a happy path and a less happy path but that's it
- Every other possible paths should be tested with Unit or Integration tests - Every other possible path should be tested with Unit or Integration tests
- Test what's displayed on the page, not the internal of ActiveRecord models - Test what's displayed on the page, not the internals of ActiveRecord models.
For instance, if you want to verify that a record was created, add
expectations that its attributes are displayed on the page, not that
`Model.count` increased by one.
- It's ok to look for DOM elements but don't abuse it since it makes the tests - It's ok to look for DOM elements but don't abuse it since it makes the tests
more brittle more brittle
...@@ -106,12 +113,12 @@ thorough testing at the System test level. ...@@ -106,12 +113,12 @@ thorough testing at the System test level.
It's very easy to add tests, but a lot harder to remove or improve tests, so one It's very easy to add tests, but a lot harder to remove or improve tests, so one
should take care of not introducing too many (slow and duplicated) specs. should take care of not introducing too many (slow and duplicated) specs.
The reason why we should follow these good practices are as follows: The reasons why we should follow these best practices are as follows:
- System tests are slow to run since they spin up the entire application stack - System tests are slow to run since they spin up the entire application stack
in a headless browser, and even slower when they integrate a JS driver in a headless browser, and even slower when they integrate a JS driver
- With System tests run with a driver that supports JavaScript, the tests are - When system tests run with a JavaScript driver, the tests are run in a
run in different thread than the application. This means it does not share a different thread than the application. This means it does not share a
database connection and your test will have to commit the transactions in database connection and your test will have to commit the transactions in
order for the running application to see the data (and vice-versa). In that order for the running application to see the data (and vice-versa). In that
case we need to truncate the database after each spec instead of simply case we need to truncate the database after each spec instead of simply
...@@ -127,7 +134,7 @@ trade-off: ...@@ -127,7 +134,7 @@ trade-off:
- Unit tests are usually cheap, and you should consider them like the basement - Unit tests are usually cheap, and you should consider them like the basement
of your house: you need them to be confident that your code is behaving of your house: you need them to be confident that your code is behaving
correctly. However if you run only unit tests without integration / system tests, you might miss the [big] [picture]! correctly. However if you run only unit tests without integration / system tests, you might miss the [big] [picture]!
- Integration tests are bit more expensive but don't abuse them. A feature test - Integration tests are a bit more expensive, but don't abuse them. A feature test
is often better than an integration test that is stubbing a lot of internals. is often better than an integration test that is stubbing a lot of internals.
- System tests are expensive (compared to unit tests), even more if they require - System tests are expensive (compared to unit tests), even more if they require
a JavaScript driver. Make sure to follow the guidelines in the [Speed](#test-speed) a JavaScript driver. Make sure to follow the guidelines in the [Speed](#test-speed)
...@@ -255,7 +262,7 @@ it 'is overdue' do ...@@ -255,7 +262,7 @@ it 'is overdue' do
end end
``` ```
### System / Features tests ### System / Feature tests
- Feature specs should be named `ROLE_ACTION_spec.rb`, such as - Feature specs should be named `ROLE_ACTION_spec.rb`, such as
`user_changes_password_spec.rb`. `user_changes_password_spec.rb`.
...@@ -297,22 +304,28 @@ them. ...@@ -297,22 +304,28 @@ them.
Our current CI parallelization setup is as follows: Our current CI parallelization setup is as follows:
1. The `knapsack` job in the prepare stage that is supposed to ensure we have a `knapsack/rspec_report.json` file: 1. The `knapsack` job in the prepare stage that is supposed to ensure we have a
- The `knapsack/rspec_report.json` file is fetched from the cache with the `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file:
`knapsack` key, if it's not here we initialize the file with `{}`. - The `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file is fetched
from S3, if it's not here we initialize the file with `{}`.
1. Each `rspec x y` job are run with `knapsack rspec` and should have an evenly 1. Each `rspec x y` job are run with `knapsack rspec` and should have an evenly
distributed share of tests: distributed share of tests:
- It works because the jobs have access to the `knapsack/rspec_report.json` - It works because the jobs have access to the
since the "artifacts from all previous stages are passed by default". [^1] `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` since the "artifacts
- the jobs set their own report path to `KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json` from all previous stages are passed by default". [^1]
- the jobs set their own report path to
`KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`.
- if knapsack is doing its job, test files that are run should be listed under - if knapsack is doing its job, test files that are run should be listed under
`Report specs`, not under `Leftover specs` `Report specs`, not under `Leftover specs`.
1. The `update-knapsack` job takes all the `knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json` files from 1. The `update-knapsack` job takes all the
the `rspec x y` jobs and merge them all together into a single `knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`
`knapsack/rspec_report.json` that is then cached with the `knapsack` key files from the `rspec x y` jobs and merge them all together into a single
`knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file that is then
uploaded to S3.
After that, the next pipeline will use the up-to-date After that, the next pipeline will use the up-to-date
`knapsack/rspec_report.json` file. `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file. The same strategy
is used for Spinach tests as well.
## Testing Rake Tasks ## Testing Rake Tasks
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment