diff options
Diffstat (limited to '')
-rw-r--r-- | browser/components/newtab/docs/v2-system-addon/unit_testing_guide.md | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/browser/components/newtab/docs/v2-system-addon/unit_testing_guide.md b/browser/components/newtab/docs/v2-system-addon/unit_testing_guide.md new file mode 100644 index 0000000000..c3dd369a2d --- /dev/null +++ b/browser/components/newtab/docs/v2-system-addon/unit_testing_guide.md @@ -0,0 +1,149 @@ +# Unit testing + +## Overview + +Our unit tests in Activity Stream are written with mocha, chai, and sinon, and run +with karma. They include unit tests for both content code (React components, etc.) +and `.jsm`s. + +You can find unit tests in `tests/unit`. + +## Execution + +To run the unit tests once, execute `npm test`. + +To run unit tests continuously (i.e. in "test-driven development" mode), you can +run `npm run tddmc`. + +## Debugging + +To debug tests, you should run them in continuous mode with `npm run tddmc`. +In the Firefox window that is opened (it should say "Karma... - connected"), +click the "debug" button and open your console to see test output, set +breakpoints, etc. + +Unfortunately, source maps for tests do not currently work in Firefox. If you need +to see line numbers, you can run the tests with Chrome by running +`npm install --save-dev karma-chrome-launcher && npm run tddmc -- --browsers Chrome` + +## Where to put new tests + +If you are creating a new test, add it to a subdirectory of the `tests/unit` +that corresponds to the file you are testing. Tests should end with `.test.js` or +`.test.jsx` if the test includes any jsx. + +For example, if the file you are testing is `lib/Foo.jsm`, the test +file should be `test/unit/lib/Foo.test.js` + +## Mocha tests + +All our unit tests are written with [mocha](https://mochajs.org), which injects +globals like `describe`, `it`, `beforeEach`, and others. It can be used to write +synchronous or asynchronous tests: + +```js +describe("FooModule", () => { + // A synchronous test + it("should create an instance", () => { + assert.instanceOf(new FooModule(), FooModule); + }); + describe("#meaningOfLife", () => { + // An asynchronous test + it("should eventually get the meaning of life", async () => { + const foo = new FooModule(); + const result = await foo.meaningOfLife(); + assert.equal(result, 42); + }); + }); +}); +``` + +## Assertions + +To write assertions, use the globally available `assert` object (this is provided +by karma-chai, so you do not need to `require` it). + +For example: + +```js +assert.equal(foo, 3); +assert.propertyVal(someObj, "foo", 3); +assert.calledOnce(someStub); +``` + +You can use any of the assertions from: + +- [`chai`](http://chaijs.com/api/assert/). +- [`sinon-chai`](https://github.com/domenic/sinon-chai#assertions) + +### Custom assertions + +We have some custom assertions for checking various types of actions: + +#### `.isUserEventAction(action)` + +Asserts that a given `action` is a valid User Event, i.e. that it contains only +expected/valid properties for User Events in Activity Stream. + +```js +// This will pass +assert.isUserEventAction(ac.UserEvent({event: "CLICK"})); + +// This will fail +assert.isUserEventAction({type: "FOO"}); + +// This will fail because BLOOP is not a valid event type +assert.isUserEventAction(ac.UserEvent({event: "BLOOP"})); +``` + +## Overriding globals in `.jsm`s + +Most `.jsm`s you will be testing use `Cu.import` or `XPCOMUtils` to inject globals. +In order to add mocks/stubs/fakes for these globals, you should use the `GlobalOverrider` +utility in `test/unit/utils`: + +```js +const {GlobalOverrider} = require("test/unit/utils"); +describe("MyModule", () => { + let globals; + let sandbox; + beforeEach(() => { + globals = new GlobalOverrider(); + sandbox = globals.sandbox; // this is a sinon sandbox + // This will inject a "AboutNewTab" global before each test + globals.set("AboutNewTab", {override: sandbox.stub()}); + }); + // globals.restore() clears any globals you added as well as the sinon sandbox + afterEach(() => globals.restore()); +}); +``` + +## Testing React components + +You should use the [enzyme](https://github.com/airbnb/enzyme) suite of test utilities +to test React Components for Activity Stream. + +Where possible, use the [shallow rendering method](https://github.com/airbnb/enzyme/blob/master/docs/api/shallow.md) (this will avoid unnecessarily +rendering child components): + +```js +const React = require("react"); +const {shallow} = require("enzyme"); + +describe("<Foo>", () => { + it("should be hidden by default", () => { + const wrapper = shallow(<Foo />); + assert.isTrue(wrapper.find(".wrapper").props().hidden); + }); +}); +``` + +If you need to, you can also do [Full DOM rendering](https://github.com/airbnb/enzyme/blob/master/docs/api/mount.md) +with enzyme's `mount` utility. + +```js +const React = require("react"); +const {mount} = require("enzyme"); +... +const wrapper = mount(<Foo />); +``` |