Skip to content

Commit 5170745

Browse files
author
Chance Strickland
committed
Add unit test for Minutes component
- Included `vanilla` example where we don't use a test runner or stubs for the DOM - Final version uses Jest and React Testing Library to query our tree
1 parent e37cc62 commit 5170745

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

src/Minutes.test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from "react";
2+
import {
3+
Minutes as MinutesRoot,
4+
MinutesAdd,
5+
MinutesSubtract,
6+
MinutesInput,
7+
} from "./Minutes";
8+
import * as ReactTestingLibrary from "@testing-library/react";
9+
10+
describe("<Minutes />", () => {
11+
describe("rendering", () => {
12+
test("that input contains accurate value based on value prop", () => {
13+
let { getByRole } = ReactTestingLibrary.render(<Minutes value={5} />);
14+
let input = getByRole("textbox");
15+
expect(input.value).toBe(String(5));
16+
});
17+
});
18+
});
19+
20+
function Minutes({ "aria-labelledby": ariaLabelledBy, ...props }) {
21+
return (
22+
<MinutesRoot {...props}>
23+
<MinutesSubtract aria-label="Subtract" />
24+
<MinutesInput />
25+
<MinutesInput aria-labelledby={ariaLabelledBy} />
26+
<MinutesAdd aria-label="Add" />
27+
</MinutesRoot>
28+
);
29+
}

src/Minutes.test.vanilla.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React from "react";
2+
import {
3+
Minutes as MinutesRoot,
4+
MinutesAdd,
5+
MinutesSubtract,
6+
MinutesInput,
7+
} from "./Minutes";
8+
import * as TestRenderer from "react-test-renderer";
9+
10+
// Just run our script immediately. We'll define it below!
11+
main();
12+
13+
// We want to write a simple unit test to make sure our Minutes component works
14+
// as expected.
15+
16+
// First of all, we are going to run this test in node, so let's start by
17+
// assuming we aren't using any thid-party tools at this point and just want to
18+
// make some assertions by running a script directly.
19+
function main() {
20+
console.log("running tests...");
21+
22+
// Now we need something that can render our component We used babel so that
23+
// Node can understand React syntax, but we don't have any way to render our
24+
// components in a way that Node can evaluate its state or output or run
25+
// assertions.
26+
//
27+
// We are also using TypeScript so we need something to transform that as
28+
// well, so we can use the babel transformer for that. Alternativelt we might
29+
// use ts-node instead of plain node, but we'd still have to deal with JSX so
30+
// babel simplifies this a bit.
31+
//
32+
// Lastly, some of our code imports css and sass for styling, which is a
33+
// problem since they aren't valid JS and Node will throw. We need to stub
34+
// those imports somehow. For simplicity here we will use Babel to do all of
35+
// this stuff for us. You can check the script we're running from package.json
36+
// and see it point to a babel config in the scripts directory.
37+
//
38+
// In the DOM we use ReactDOM, and in a test environment we probably want to
39+
// reach for something similar but designed for node. For now we'll start with
40+
// React Test Renderer. Create the renderer for our test first.
41+
const testRenderer = TestRenderer.create(<Minutes value={5} />);
42+
43+
// Just to start, let's see what the renderer gives us as a JSON object.
44+
// Uncomment these if you want to inspect the output
45+
// let tree = testRenderer.toJSON();
46+
// console.log(tree);
47+
48+
// The renderer here can do some other nice things for us. Let's get an
49+
// instance of the renderer we can use to quickly access items in the
50+
// rendered results tree.
51+
let testInstance = testRenderer.root;
52+
53+
// When we render this component we can see we have an object representation
54+
// of the DOM tree that the component renders.
55+
56+
// What we probably want right out of the gate is a test that makes sure that
57+
// a valid minutes prop renders the value to the input.
58+
59+
// We can see we have a reference to the input in our rendered object.
60+
// TestRenderer has a handy utility we can use to snag a reference to that
61+
// because we know the type of our input is...an input.
62+
let input = testInstance.findByType("input");
63+
64+
// Now we can check that the value that was ultimately passed to our input
65+
// matches the value we passed to the minutes prop in the Minutes component
66+
// when we rendered it to our instance.
67+
//
68+
// Let's write a handy assertion function here for a nice readable statement
69+
// for our tests.
70+
expect(input.props.value).toBe(5);
71+
72+
// If we make it to here, we're finished!
73+
console.log("Tests passed!");
74+
75+
// Now we have a working test for this case! But this wasn't a ton of fun so
76+
// let's try bringing in some tooling instead :)
77+
}
78+
79+
function Minutes({ "aria-labelledby": ariaLabelledBy, ...props }) {
80+
return (
81+
<MinutesRoot {...props}>
82+
<MinutesSubtract aria-label="Subtract" />
83+
<MinutesInput />
84+
<MinutesInput aria-labelledby={ariaLabelledBy} />
85+
<MinutesAdd aria-label="Add" />
86+
</MinutesRoot>
87+
);
88+
}

0 commit comments

Comments
 (0)