Skip to content

Commit f6c0a62

Browse files
committed
README and inspiration and solution
1 parent 10d193b commit f6c0a62

File tree

7 files changed

+272
-0
lines changed

7 files changed

+272
-0
lines changed

lesson-3/README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# React Testing
2+
3+
## The Goal
4+
5+
Learn about different types of automated tests and how to apply them in React environment.
6+
7+
## Automated tests
8+
9+
![Test pyramid by Martin Fowler](https://martinfowler.com/bliki/images/testPyramid/test-pyramid.png)
10+
11+
[The test pyramid](https://martinfowler.com/bliki/TestPyramid.html)
12+
13+
- **End To End tests**: Slow, complicated, brittle, complete
14+
- **Unit tests**: Fast, simple, reliable, isolated
15+
16+
## How to write a good test
17+
18+
1. Write a failing test
19+
2. Observe that it actually fails!
20+
3. Make sure that it fails with red
21+
4. Fix code so that the test passes
22+
5. GOTO 1
23+
24+
## How to write a code that is easy to test
25+
26+
**Pure functions** for the win:
27+
- Given an input, always produces the same output
28+
- No side effects (including changing its arguments)
29+
30+
React Components are usually pure functions. Not a coincidence!
31+
32+
## Tools that we will use
33+
34+
- [Jest - Delightful JavaScript Testing](https://facebook.github.io/jest/) framework and toolset for testing
35+
- [Enzyme](https://github.com/airbnb/enzyme) library for testing React components
36+
37+
## Tools that we will not use
38+
39+
**E2E testing**: Selenium, Webdrivers, Headless browsers, Robot Framework (because they take too much time to set up and learn)
40+
41+
**Unit testing**: Mocha, Chai, Expect, Istanbul, Sinon (because these are alternatives to Jest)
42+
43+
**Server/API testing**: Supertest (because we focus on frontend only, for now)
44+
45+
## Hands on!
46+
47+
_Excercise 1: Checkout lesson-3 folder and run the app! `npm install`, `npm start`_
48+
49+
_Excercise 2: Find your favourite bug_
50+
51+
_Excercise 3: Write a unit test and `npm test`_
52+
53+
https://facebook.github.io/jest/docs/en/expect.html
54+
55+
Hint:
56+
```javascript
57+
import { removeFromArray } from "./functions"
58+
59+
// test suite: organize your tests!
60+
describe("functions.test.js: removeFromArray", () => {
61+
62+
// single test
63+
it("should remove item from array", () => {
64+
const input = ... // prepare data
65+
const actual = ... // call the function here
66+
const expected = ... // what you want to see?
67+
expect(actual).toEqual(expected) // test!
68+
})
69+
})
70+
```
71+
72+
_Excercise 4: Write a Component test using enzyme_
73+
74+
http://airbnb.io/enzyme/docs/api/shallow.html
75+
76+
https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#testing-components
77+
78+
```javascript
79+
import React from "react" // because we will use JSX
80+
import Party from "./Party"
81+
import { shallow } from "enzyme" // there are multiple kinds of rendering
82+
83+
describe("Party.js", () => {
84+
85+
it("should display party name", () => {
86+
const party = { name: "MyParty", members: 100 } // some data that we pass to props
87+
const wrapper = shallow(<Party party={party} />)
88+
const text = wrapper.text()
89+
expect(text).toMatch("MyParty")
90+
})
91+
92+
})
93+
```
94+
95+
_Excercise 5: Write propTypes_
96+
97+
Technically, this is not a test. But it will help you!
98+
99+
https://reactjs.org/docs/typechecking-with-proptypes.html
100+
101+
102+
103+
## Reading and more links
104+
105+
(You may think I am biased towards Eric Elliot - perhaps. But he does write well!)
106+
107+
- [5 Questions Every Unit Test Must Answer](https://medium.com/javascript-scene/what-every-unit-test-needs-f6cd34d9836d)
108+
- [5 Common Misconceptions About TDD & Unit Tests](https://medium.com/javascript-scene/5-common-misconceptions-about-tdd-unit-tests-863d5beb3ce9)
109+
- [TDD the RITE Way](https://medium.com/javascript-scene/tdd-the-rite-way-53c9b46f45e3)
110+
- [Learn Test Driven Development (TDD)](https://github.com/dwyl/learn-tdd)
111+
- https://github.com/msd-code-academy/03-testing-react-app
112+
- [What is a Pure Function?](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-pure-function-d1c076bec976)
113+
- [Pure Happiness with Pure Functions](https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch3.html)

lesson-3/inspiration/Party.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React, { PureComponent } from "react"
2+
import PropTypes from "prop-types"
3+
import "./Party.css"
4+
5+
export default class Party extends PureComponent {
6+
7+
selectParty = (e) => {
8+
this.props.partySelected(this.props.party)
9+
}
10+
render() {
11+
const { party } = this.props
12+
return (
13+
<a onClick={this.selectParty} key={party.name} className="Party" style={{ borderColor: party.color }}>
14+
<h2>{party.name}</h2>
15+
<p>
16+
Members in parliament: {party.name}
17+
</p>
18+
</a>
19+
)
20+
}
21+
}
22+
23+
Party.propTypes = {
24+
}
25+
26+
Party.defaultProps = {
27+
}

lesson-3/inspiration/Party.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from "react"
2+
import Party from "./Party"
3+
import { shallow } from "enzyme"
4+
5+
describe("Party.js", () => {
6+
it("should render", () => {
7+
shallow(<Party party={{}} />)
8+
})
9+
10+
it("should display party name")
11+
12+
it("should display amount of members")
13+
14+
it("should match snapshot")
15+
16+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { removeFromArray } from "./functions"
2+
3+
describe("functions.test.js removeFromArray", () => {
4+
it.skip("should remove item from array", () => {
5+
// TODO implement test here and remove `.skip`
6+
})
7+
8+
// Tests without callback function are automatically pending (as an alternative to .skip)
9+
it("should work with empty array")
10+
11+
it("should not remove anything if needle does not exist")
12+
13+
it("should not modify the parameter passed (immutable)")
14+
15+
it("should throw Errors when passed non-Array")
16+
})

lesson-3/solution/Party.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, { PureComponent } from "react"
2+
import PropTypes from "prop-types"
3+
import "./Party.css"
4+
5+
export default class Party extends PureComponent {
6+
7+
selectParty = (e) => {
8+
this.props.partySelected(this.props.party)
9+
}
10+
render() {
11+
const { party } = this.props
12+
return (
13+
<a onClick={this.selectParty} key={party.name} className="Party" style={{ borderColor: party.color }}>
14+
<h2>{party.name}</h2>
15+
<p>
16+
Members in parliament: {party.name}
17+
</p>
18+
</a>
19+
)
20+
}
21+
}
22+
23+
Party.propTypes = {
24+
party: PropTypes.shape({
25+
name: PropTypes.string.isRequired,
26+
members: PropTypes.number.isRequired,
27+
color: PropTypes.string
28+
}),
29+
partySelected: PropTypes.func
30+
}
31+
32+
Party.defaultProps = {
33+
partySelected: () => {
34+
/* Do nothing */
35+
}
36+
}

lesson-3/solution/Party.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from "react"
2+
import Party from "./Party"
3+
import { shallow } from "enzyme"
4+
5+
describe("Party.js", () => {
6+
it("should render", () => {
7+
shallow(<Party party={{}} />)
8+
})
9+
10+
it("should display party name", () => {
11+
const party = { name: "MyParty", members: 100 }
12+
const wrapper = shallow(<Party party={party} />)
13+
const text = wrapper.text()
14+
expect(text).toMatch("MyParty")
15+
})
16+
17+
it("should display amount of members", () => {
18+
const party = { name: "MyParty", members: 100 }
19+
const wrapper = shallow(<Party party={party} />)
20+
const text = wrapper.text()
21+
expect(text).toMatch("100")
22+
})
23+
24+
it("should match snapshot", () => {
25+
const party = { name: "MyParty", members: 100 }
26+
const wrapper = shallow(<Party party={party} />)
27+
const component = wrapper.debug()
28+
expect(component).toMatchSnapshot()
29+
})
30+
31+
})

lesson-3/solution/functions.test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { removeFromArray } from "./functions"
2+
3+
describe("functions.test.js removeFromArray", () => {
4+
it("should remove item from array", () => {
5+
const arr = [1, 2, 3, 4]
6+
const expected = [1, 2, 4]
7+
const actual = removeFromArray(arr, 3)
8+
expect(actual).toEqual(expected)
9+
})
10+
11+
it("should work with empty array", () => {
12+
const actual = removeFromArray([], 1)
13+
expect(actual).toEqual([])
14+
})
15+
16+
it("should not remove anything if needle does not exist", () => {
17+
const arr = [1, 2, 3, 4]
18+
const actual = removeFromArray(arr, "Karel")
19+
const expected = [1, 2, 3, 4]
20+
expect(actual).toEqual(expected)
21+
})
22+
23+
it("should not modify the parameter passed (immutable)", () => {
24+
const arr = [1, 2]
25+
const actual = removeFromArray(arr, 1)
26+
expect(arr).toEqual([1, 2]) // the original array is not modified
27+
})
28+
29+
it("should throw Errors when passed non-Array", () => {
30+
expect(() => removeFromArray(undefined, null))
31+
.toThrow()
32+
})
33+
})

0 commit comments

Comments
 (0)