describe-component
describe-component
is an easy-to-use React unit testing library that removes
all your boilerplate code from your tests.
It codifies a pattern for unit testing using
Enzyme so that your tests are all
consistently written. With describe-component
, anyone can read, understand,
and change a unit test for a React component.
Here's what it looks like. This example is for Jest, but
describe-component
also works in Mocha, Jasmine,
AVA, or
any other test framework with beforeEach and afterEach.
;; const ColorableDiv = <div data-component-name="ColorableDiv" style=color ? color : undefined> children </div>; ;
Here's how it works:
- You call
describeComponent
with a component class or function and a callback. - Your callback is synchronously called with a set of helper functions.
- In your callback, you use the
setProps
andclearProps
helpers to set the props you want to render your component with. - In your callback, you call
mountWrapper
,shallowWrapper
, orrenderWrapper
to use Enzyme'smount
,shallow
, orrender
functions on the component you are testing, using the props you set earlier.
Installation
With yarn:
$ yarn add --dev describe-component
With npm:
$ npm install --save-dev describe-component
If you don't have them already, you will also need React and Enzyme installed. See the Enzyme installation instructions for info on how to install Enzyme.
Documentation
describeComponent
describeComponent(Component, callbackFunction) => undefined
Creates a wrapping describe
for the Component's display name, sets up a bunch
of boilerplate, and calls back your callbackFunction
.
// This...; // ... is roughly the same as this:; ;
Your callbackFunction
gets called with an object that has the
following helper methods on it: mountWrapper
,
shallowWrapper
, renderWrapper
,
setProps
, clearProps
, and props
.
mountWrapper
mountWrapper([enzymeOptions]) => ReactWrapper
A wrapper around Enzyme's mount
that will mount your component (using the props set by setProps
and
clearProps
), and return the
ReactWrapper
created by mount
.
// This...const card = ; // ... is roughly the same as this:;
If present, the options passed into mountWrapper will be passed into Enzyme's
mount
as the second argument.
// This...const card = ; // ... is roughly the same as this:;
mountWrapper
is memoized, so it will only call mount
once per test, and
subsequent calls will return the first ReactWrapper instance.
=== ; // true
This means that you can use mountWrapper
as many times as you want without any
performance penalty:
;;
Usually when you use mountWrapper
, you rename it so that its name matches the
name of the component under test, but written in lowerCamelCase:
;
The ReactWrapper
created by mountWrapper
will be unmounted automatically
after each test.
shallowWrapper
shallowWrapper([enzymeOptions]) => ShallowWrapper
A wrapper around Enzyme's shallow
that will shallow-render your component (using the props set by setProps
and clearProps
), and return the
ShallowWrapper
created by shallow
.
// This...const flavor = ; // ... is roughly the same as this:;
If present, the options passed into shallowWrapper will be passed into Enzyme's
shallow
as the second argument.
// This...const flavor = ; // ... is roughly the same as this:;
shallowWrapper
is memoized, so it will only call shallow
once per test, and
subsequent calls will return the first ShallowWrapper instance.
=== ; // true
This means that you can use shallowWrapper
as many times as you want without
any performance penalty:
;;
Usually when you use shallowWrapper
, you rename it so that its name matches
the name of the component under test, but written in lowerCamelCase:
;
The ShallowWrapper
created by shallowWrapper
will be unmounted automatically
after each test.
renderWrapper
renderWrapper([enzymeOptions]) => CheerioWrapper
A wrapper around Enzyme's render
that will render your component to static markup (using the props set by
setProps
and clearProps
), and return the
CheerioWrapper
created by render
.
// This...const shades = ; // ... is roughly the same as this:;
If present, the options passed into renderWrapper will be passed into Enzyme's
render
as the second argument.
// This...const shades = ; // ... is roughly the same as this:;
Unlike mountWrapper
and shallowWrapper
,
renderWrapper
is NOT memoized, so it will call render
every time you call
renderWrapper
.
=== ; // false
Usually when you use renderWrapper
, you rename it so that its name matches
the name of the component under test, but written in lowerCamelCase:
;
setProps
setProps(Object) => undefined
A function which sets the props to render the component with.
You call it with an object whose key/value pairs correspond to prop names and values:
;// The props are now wantsShampoo={true} deluxeCut={false}
Once you've called it, you can then use mountWrapper
,
shallowWrapper
, or renderWrapper
to
render the component.
;;
If you call it more than once, it does NOT replace the existing props; instead,
it shallowly mixes the props you pass into the existing props, much like a
React Component's setState
method:
;;// The props are now pedicure={true} manicure={false}
This behavior is useful when using nested describes to describe different states your component can be in as a result of the props it received:
;
If you want to clear all the props, use the clearProps
function.
When using mountWrapper
or
shallowWrapper
, you may only use setProps
before the
first time you call mountWrapper
/shallowWrapper
. If you try to use it after
the component has rendered, an error will be thrown. If you want change the
props of an already-mounted component, you should use the setProps
method on
the ReactWrapper
/ShallowWrapper
returned from mountWrapper
/shallowWrapper
instead:
// instead of this;; // ❌ ERROR! // do this;; // All good! 👍
The reason describe-component
doesn't treat these two forms interchangeably is
that changing the props of an already-rendered component will go through a
different code path (componentWillReceiveProps
) than setting the props for a
component before mounting it (componentWillMount
), so it's important not
to mix up the two.
Component { console; } { console; }
However, when using renderWrapper
, calling setProps
after
rendering is totally fine:
;; // Sure thing! 😀
The decision was made to allow this in this case because a
CheerioWrapper
has some significant differences when compared to a
ReactWrapper
or
ShallowWrapper
:
- A
CheerioWrapper
contains static HTML markup, with no knowledge of React or component lifecycle methods. All your component instances go away once rendering is complete. - Because of this, a
CheerioWrapper
does not have asetProps
method on it.
These differences would make a CheerioWrapper
hard to work with if you were
not allowed to call setProps
after rendering one:
// Okay, I want to verify that this component has the same html when// I give it the prop colors={["white", "gold"]} as when I give it// colors={["blue", "black"]}.;
clearProps
clearProps() => undefined
A function which will clear all the props set by setProps
.
;// props is now chunky="bacon";// props is now empty
The same rules apply to clearProps
as they do to setProps
:
namely, you cannot call clearProps
after mountWrapper
or
shallowWrapper
have been called.
props
props() => Object
A function which will return the current props.
;; // { one: "two" };; // { one: "two", three: "four" };; // { one: "two", three: "four", can: "I", have: "a", little: "more" }
This can be useful when you want to write an assertion that props got spread onto a rendered component without checking every single prop key and value:
const TheGreatDelegator = <a href="https://www.xkcd.com/1790/" ...props> No YOU deal with this </a> ;
It can also be useful when you want to verify that a specific prop was threaded through to a rendered component, without saving the value of the prop in a variable:
const GoodLuckClickingThis = <div onClick=onClick />; ;
Usage (Test Runner Configuration)
Jest
;; ;
See also the Jest example.
Mocha
;; ;
See also the Mocha example.
Jasmine
;; ;
See also the Jasmine example.
AVA
;; const describeComponent = test; ;
See also the AVA example.
Generic/Other
// If your test runner isn't listed or supported yet, you can configure// describe-component manually to work with it as long as it has support for// beforeEach/afterEach hooks.;; const describeComponent = ; // Call describeComponent normally;
See also the way it was done for AVA.
License
MIT