Testing Effects
Effects provide a way to control side-effects. A side-effect may include persistence in localStorage, updating a page title, or some other mutative behavior.
This can be a challenge for testing, as a side-effect may mutate a globally available piece of state.
Wrangling Effects
The Jest test runner provides a sandbox for every test suite. This helps us deal with the issues of stateful testing – particularly in the DOM.
By using the jsdom environment setting, Jest can setup a unique DOM for every test. This can be enabled via the --env CLI flag, or env configuration option:
# We might run our tests like:
jest --env jsdomWith that in mind, let’s jump into an example
Testing an Effect
In our guide on Effects, we went through an example that updates the query string parameter of the URL. Recall our Location effect:
import url from 'url'
import {patch} from '../actions/query'
class Location {
updateQuery (repo) {
const { origin, hash } = window.location
const location = url.format({
host : origin,
query : repo.state.query,
hash : hash
})
window.history.pushState(null, null, location)
}
register () {
return {
[patchQuery] : this.updateQuery
}
}
}
export default LocationSo in the case above, whenever a patchQuery action resolves, it’ll run through some logic to update the URL location. Triggering that might look something like:
import Microcosm from 'microcosm'
import Location from './effects/location'
import {patchQuery} from './actions/query'
let repo = new Microcosm()
repo.addEffect(Location)
// The query string will now be ?page=10
repo.push(patchQuery, { page: 10 })With that out of the way, let’s move into testing.
Teaching Jest about URLs
This test will require using the browser’s pushState API, that means we need to teach Jest to operate at a test URL. Otherwise, you’ll see an error that looks something like:
SecurityError
at HistoryImpl._sharedPushAndReplaceState (~/jsdom/...)Not good. So let’s fix that! Jest keeps all of it’s configuration in a JSON file. By default, this is the package.json for your project:
// ... config
"jest": {
"testURL": "http://example.com"
}
// ... configThat should do it for configuration. Now let’s move on to a test!
A basic test
The simplest thing would be to just reproduce our usage example as a test:
import Microcosm from 'microcosm'
import Location from './effects/location'
import {patchQuery} from './actions/query'
describe('Location Effect', function () {
it('it updates the URL when a repo is sent patchQuery', function() {
let repo = new Microcosm()
repo.addEffect(Location)
repo.push(patchQuery, { page: 10 })
expect(window.location.search).toContain('page=10')
})
})toContain will check to see if one string is a part of another. In this case, it’s checking to see if the URL parameters are what we expect.
Controlling for change
Our previous test is great, but how do we prevent future tests from being affected by those that have already run?
Jest provides a beforeEach function that makes it easy to perform a bit of work before every test runs. We can use it to set up our test:
import Microcosm from 'microcosm'
import Location from './effects/location'
import {patchQuery} from './actions/query'
describe('Location Effect', function () {
beforeEach(function () {
window.location.search = ''
})
// ...
})Nice! Now every test will start with a clean slate!
Wrapping up
The important thing to remember when testing Effects is to control for the destructive changes they may apply. By setting up your testing environment to sandbox their changes, testing Effects becomes much easier.