Skip to content

Instantly share code, notes, and snippets.

@shuhei
Last active August 8, 2019 22:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shuhei/302ca48e2972a8b7b9baa33379b56c52 to your computer and use it in GitHub Desktop.
Save shuhei/302ca48e2972a8b7b9baa33379b56c52 to your computer and use it in GitHub Desktop.
An idea for testing custom React hooks
const React = require("react");
const { mount } = require("enzyme");
// A custom hook to test
function useCounter(initial) {
const [count, setCount] = React.useState(initial);
const increment = React.useCallback(() => setCount(c => c + 1), []);
const reset = React.useCallback(() => setCount(initial), [initial]);
return { count, increment, reset };
}
// A component for testing the hook
function CounterTest({ initial }) {
const { count, increment, reset } = useCounter(initial);
return (
<div>
<div id="count">{count}</div>
<button id="increment" onClick={increment}>increment</button>
<button id="reset" onClick={reset}>reset</button>
</div>
);
}
test("counter", () => {
const wrapper = mount(<CounterTest initial={123} />);
expect(wrapper.find("#count").text()).toEqual("123");
// `increment` should increment the count.
wrapper.find("#increment").simulate("click");
expect(wrapper.find("#count").text()).toEqual("124");
wrapper.find("#increment").simulate("click");
expect(wrapper.find("#count").text()).toEqual("125");
// `reset` should reset the count with the initial value.
wrapper.find("#reset").simulate("click");
expect(wrapper.find("#count").text()).toEqual("123");
// `reset` should use the latest initial value.
wrapper.setProps({ initial: 234 });
expect(wrapper.find("#count").text()).toEqual("123");
wrapper.find("#reset").simulate("click");
expect(wrapper.find("#count").text()).toEqual("234");
});
const React = require("react");
const { mount } = require("enzyme");
// A custom hook to test
function useCounter(initial) {
const [count, setCount] = React.useState(initial);
const increment = React.useCallback(() => setCount(c => c + 1), []);
const reset = React.useCallback(() => setCount(initial), [initial]);
return { count, increment, reset };
}
// A component for testing the hook
function CounterTest({ initial }) {
const { count, increment, reset } = useCounter(initial);
return render({ count, increment, reset });
}
test("counter", () => {
const wrapper = mount(<CounterTest initial={123} />);
expect(getValue(wrapper, "count")).toEqual(123);
// `increment` should increment the count.
callFunction(wrapper, "increment");
expect(getValue(wrapper, "count")).toEqual(124);
callFunction(wrapper, "increment");
expect(getValue(wrapper, "count")).toEqual(125);
// `reset` should reset the count with the initial value.
callFunction(wrapper, "reset");
expect(getValue(wrapper, "count")).toEqual(123);
// `reset` should use the latest initial value.
wrapper.setProps({ initial: 234 });
expect(getValue(wrapper, "count")).toEqual(123);
callFunction(wrapper, "reset");
expect(getValue(wrapper, "count")).toEqual(234);
});
// Helper functions
function render(fields) {
const items = Object.entries(fields).map(([name, value]) => {
if (typeof value === "function") {
const onClick = (args) => {
value.apply(null, args);
};
return <button id={name} onClick={onClick}>{JSON.stringify(value)}</button>;
}
return <div id={name}>{value}</div>;
});
return <div>{items}</div>;
}
function getValue(wrapper, name) {
return JSON.parse(wrapper.find(`#${name}`).text());
}
function callFunction(wrapper, name, args = []) {
wrapper.find(`#${name}`).simulate('click', args);
}
const React = require("react");
const { mount } = require("enzyme");
// A custom hook to test
function useCounter(initial) {
const [count, setCount] = React.useState(initial);
const increment = React.useCallback(() => setCount(c => c + 1), []);
const reset = React.useCallback(() => setCount(initial), [initial]);
return { count, increment, reset };
}
// A component for testing the hook
function CounterTest({ initial }) {
const { count, increment, reset } = useCounter(initial);
return render({ count, increment, reset });
}
test("counter", () => {
const wrapper = mount(<CounterTest initial={123} />);
const hook = makeHook(wrapper);
expect(hook.count).toEqual(123);
// `increment` should increment the count.
hook.increment();
expect(hook.count).toEqual(124);
hook.increment();
expect(hook.count).toEqual(125);
// `reset` should reset the count with the initial value.
hook.reset();
expect(hook.count).toEqual(123);
// `reset` should use the latest initial value.
wrapper.setProps({ initial: 234 });
expect(hook.count).toEqual(123);
hook.reset();
expect(hook.count).toEqual(234);
});
// Helper functions
function render(fields) {
const items = Object.entries(fields).map(([name, value]) => {
if (typeof value === "function") {
const onClick = (args) => {
value.apply(null, args);
};
return (
<button
id={name}
key={name}
onClick={onClick}
>{JSON.stringify(value)}</button>
);
}
return (
<div
id={name}
key={name}
>{value}</div>
);
});
return <div>{items}</div>;
}
function makeHook(wrapper) {
return new Proxy({}, {
get(target, prop) {
const element = wrapper.find(`#${prop}`);
if (element.is('div')) {
return JSON.parse(element.text());
} else {
return (args) => {
element.simulate('click', args);
};
}
}
});
}
const React = require("react");
const { mount } = require("enzyme");
// A custom hook to test
function useCounter(initial) {
const [count, setCount] = React.useState(initial);
const increment = React.useCallback(() => setCount(c => c + 1), []);
const reset = React.useCallback(() => setCount(initial), [initial]);
return { count, increment, reset };
}
test("counter", () => {
const { hook, update, act } = makeHook(useCounter, 123);
expect(hook.count).toEqual(123);
// `increment` should increment the count.
hook.increment();
expect(hook.count).toEqual(124);
hook.increment();
expect(hook.count).toEqual(125);
// `reset` should reset the count with the initial value.
hook.reset();
expect(hook.count).toEqual(123);
// `reset` should use the latest initial value.
update(234);
expect(hook.count).toEqual(123);
hook.reset();
expect(hook.count).toEqual(234);
});
function makeHook(useHook, ...args) {
let current;
function TestComponent({ args }) {
current = useHook(...args);
const callFunc = ({ func }) => {
func();
};
return (
<button id="call-me" onClick={callFunc}>call me</button>
);
}
const wrapper = mount(<TestComponent args={args} />);
const update = (...args) => {
wrapper.setProps({ args });
};
const act = (func) => {
wrapper.find("#call-me").simulate("click", { func });
};
const hook = new Proxy({}, {
get(_target, prop) {
const value = current[prop];
if (typeof value === "function") {
return (...args) => {
act(() => value(...args));
};
} else {
return value;
}
}
});
return {
hook,
update,
act
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment