ferp-js
Search…
Testing Actions

State Change

Testing for a state change is easy, and you can just run your action like a function.
1
import { effects } from 'ferp';
2
3
const IncrementCounter = state => [
4
{ ...state, counter: state.counter + 1 },
5
effects.none(),
6
];
7
8
describe('IncremenetCounter', () => {
9
it('increments the counter state variable', () => {
10
const initialState = { counter: 0 };
11
12
const [state, _effect] = IncrementCounter(initialState);
13
14
expect(state).toDeepEqual({
15
counter: 1,
16
});
17
});
18
});
Copied!
Testing action builders is very similar, too:
1
import { effects } from 'ferp';
2
3
const IncrementCounterByN = n => state => [
4
{ ...state, counter: state.counter + n },
5
effects.none(),
6
];
7
8
describe('IncremenetCounterByN', () => {
9
it('increments the counter state variable using the provided value', () => {
10
const initialState = { counter: 0 };
11
12
const [state, _effect] = IncrementCounterByN(999)(initialState);
13
14
expect(state).toDeepEqual({
15
counter: 999,
16
});
17
});
18
});
Copied!

Result of Side-Effects

Testing that the correct side effects can be a little more tricky. The problem is that effects touch the outside world that is not controlled by Ferp. Running these end-to-end just like the effect testing is likely your best bet.
actions.spec.js
actions.js
todoFx.js
1
import * as sinon from 'sinon';
2
import { app } from 'ferp';
3
4
import * as actions from './actions.js';
5
6
describe('RequestTodo', () => {
7
it('does not modify the state', () => {
8
const initialState = { todos: [], externals: { fetch: window.fetch } };
9
10
const [state] = actions.RequestTodo(1)(initialState);
11
12
expect(state).toBe(initialState);
13
});
14
15
it('successfully fetches a todo', (done) => {
16
const expectedTodo = { id: 1, text: 'foo', completed: false };
17
const fakeFetch = sinon.fake((arg) => {
18
return {
19
json: Promise.resolve(expectedTodo)
20
};
21
});
22
const initialState = { todos: [], externals: { fetch: fakeFetch } };
23
24
const expectedTodosInOrder = [
25
[], // init
26
[], // request
27
[expectedTodo], // on success
28
];
29
30
app({
31
init: actions.RequestTodo(expectedTodo.id)(initialState),
32
observe: ([state, effect]) => {
33
expect(state.todos)
34
.toDeepEqual(expectedTodosInOrder.unshift());
35
36
if (expectedTodosInOrder.length === 0) {
37
done();
38
}
39
},
40
});
41
});
42
});
Copied!
1
import { effects } from 'ferp';
2
import * as todoFx from './todoFx.js';
3
4
export const AddTodo = (todo) => (state) => [
5
{ ...state, todos: state.todos.concat(todo) },
6
effects.none(),
7
];
8
9
export const RequestTodo = (todoId) => (state) => [
10
state,
11
todoApi.getTodo(state.external.fetch, todoId, AddTodo),
12
];
Copied!
1
import { effects } from 'ferp';
2
3
export const getTodo = (fetchFn, id, onSuccess, onFailure) => effects.defer(
4
fetchFn(`https://jsonplaceholder.typicode.com/todos/${id}`)
5
.then(response => response.json())
6
.then(data => effects.act(onSuccess(data)))
7
.catch((err) => (
8
onFailure
9
? effects.act(onFailure(err))
10
: effects.none()
11
))
12
);
Copied!
Yes, that test code looks clunky, so why not make a helper to make things a little easier?
support/endToEndTest.js
1
import { app } from 'ferp';
2
3
export const endToEndTest = (init, assertions, done) => app({
4
init,
5
observe: (...args) => {
6
const assertion = assertions.unshift();
7
assertion(...args);
8
if (assertions.length === 0) {
9
done();
10
}
11
},
12
});
Copied!
Which would result in:
actions.spec.js
1
import { endToEndTest } from './support/endToEndTest.js';
2
3
// ...
4
5
it('successfully fetches a todo', (done) => {
6
const expectedTodo = { id: 1, text: 'foo', completed: false };
7
const fakeFetch = sinon.fake((arg) => {
8
return {
9
json: Promise.resolve(expectedTodo)
10
};
11
});
12
const initialState = { todos: [], externals: { fetch: fakeFetch } };
13
14
endToEndTest(
15
actions.RequestTodo(expectedTodo.id)(initialState),
16
[
17
([state]) => expect(state.todos).toDeepEqual([]), // init
18
([state]) => expect(state.todos).toDeepEqual([]), // request
19
([state]) => expect(state.todos).toDeepEqual([expectedTodo]), // on success
20
],
21
done
22
);
23
});
Copied!
There may be other forms of helpers that can make the end-to-end portions of your tests to be more readable and manageable.
Last modified 2mo ago