# Testing ## Unit testing - [Better specs](http://www.betterspecs.org/) (although created for RSpec and Ruby) contains many good general principles - [Rubocop's guide on test naming](https://github.com/rubocop-hq/rspec-style-guide#naming) (although created for RSpec and Ruby) contains many good general principles - Test coverage aim: 100%. This doesn't mean that the build should fail if it's 99, but specifying a lower aim makes no sense as the goal is to have everything covered - Tests are written to test, not to increase coverage - Their scope is very limited (to not bleed into other code, both functionality and coverage wise) and it is clear what they test ### Naming - Name of testfile is the same as name of tested file. Additional suffex might be added as per framework conventions. E.g. - JS with Jest: `getProxyURL.js` => `getProxyURL.test.js` - Ruby with RSpec: `get_proxy_url.js` => `get_proxy_url_spec.js` - Test are described in 3 parts: `describe`, `context`, `it` - `describe` simply states the name of the component / function - `context` elaborates on additional state / setup - only if needed - always starts with `when`, `with` or `without` - forms a sentence with proper grammar when composed with `it` block descriptions - uses `context` function (or `describe` if the former is not available) - does *not* describe action, only state (e.g. correct: when button is inactive. incorrect: when button is clicked) - usually has a counter-example with the opposite state - `it` states what the tested piece of logic (referenced by describe) does in the given context Practical example: ```js `describe('getProxyURL', () => { describe('when CORS_PROXY_ENABLED env var is true', () => { it('returns the given URL with a CORS proxy', () => { # test }); }); }); ``` This read nicely as: `describe getProxyURL: when CORS_PROXY_ENABLED env var is true, it returns the given URL with a CORS proxy` Always double check whether the test actually does what the description says. E.g. ``` describe ('delete button, () => { it ('removes the item`, () => { originalLength = items.length; wrapper.find('deleteButton').simulate('click'); expect(items).toHaveLength(originalLength - 1); }); }); ``` This test desciption is *not* correct. It checks that when the delete button is clicked on an item, it removes *an* item, not necessarily *the* item. Tests descriptions are there so that we don't have to look at the tests themselves, but that's only the case if they're accurate. It might be fine to have a test like the above, but the description should read e.g. `it descreases the number of items by 1`. ### Independence of tests Tests are always independent of each other. Best practices include - using per test case context setups (i.e. before each, not before all) - resetting common storages between tests (e.g. DB, locale storage) - resetting mocks between tests It is enforced by running tests isolated or in a randomized order. ### Mocking / stubbing / spying Should be used - to skip unreliable / slow parts of the code (e.g. network requests) Could be used - to avoid testing code _written for the project_ which is not the subject of the test - to avoid indeterminate functionality GOOD ``` it "creates user with a unique key" do key = 'dummy_unique' allow(UniqueGenrator).to receive(:hex).and_return(key) expect(User.create.key).to eq(key) end ``` Should _not_ be used - unless there's a reason to use them (a.k.a. this is not the default approach to testing) - to skip 3rd party code (e.g. DB, framework) - as a replacement for verifying return values GOOD ``` get :index expect(response).to be_successful ``` BAD ``` expect(response).to receive(:send_status).with(200) get :index ``` - as a replacement for verifying side effects GOOD ``` deleteUser(id) expect(User.find(id)).not_to be ``` BAD ``` expect(User).to receive(:delete).with(id) deleteUser(id) ```