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 jsdom
With 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 Location
So 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"
}
// ... config
That 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.