Testing
Now that everything is working, let's make sure that it stays that way!
Packages to install
There are 3 packages I recommend you'll install:
- pouchdb-adapter-memory
- react-hooks-testing-library
- React Testing Library (pre installed with Create-React-App)
npm i -D pouchdb-adapter-memory @testing-library/react-hooks @testing-library/react
yarn add -D pouchdb-adapter-memory @testing-library/react-hooks @testing-library/react
pouchdb-adapter-memory
pouchdb-adapter-memory is a PouchDB Adapter. Adapters translate how your documents are stored and accessed. For example pouchdb-adapter-http (pre installed on most PouchDB setups) translates them into CouchDB's HTTP API.
But pouchdb-adapter-memory only stores them in memory. Perfect or testing!
react-hooks-testing-library
react-hooks-testing-library is a really good testing library, specialized in testing React Hooks.
The documentation for it can be found on https://react-hooks-testing-library.com/.
You can use it to test your own hooks. Be they extensions of hooks from usePouchDB
or your own.
React Testing Library
React Testing Library comes pre-installed with create-react-app. It is a testing library that encourage you to test from the users perspective.
The documentation for it can be found at https://testing-library.com/docs/react-testing-library/intro.
Tests
Your tests should be closed systems. And no test before or after it should influence them. To achieve this I
recommend that you create a new PouchDB database before each test. And that db should have the
pouchdb-adapter-memory
active.
// Using jest
import PouchDB from "pouchdb";
import memory from "pouchdb-adapter-memory";
// add the adapter to PouchDB
PouchDB.plugin(memory);
let myPouch = null;
beforeEach(() => {
// before each test, create a new DB with the memory adapter.
// nothing will be saved on disc!
myPouch = new PouchDB("test", { adapter: "memory" });
});
afterEach(async () => {
// Destroy the database after each test,
// so that no data will be left from the previous test.
await myPouch.destroy();
});
Also remember, that you must add all needed document before/during the test!
Components
All hooks must be in child components of <Provider />
.
For React Testing Library you can warp your component with <Provider />
:
import React from "react";
import { render } from "@testing-library/react";
import PouchDB from "pouchdb";
import memory from "pouchdb-adapter-memory";
import TodoList from "./TodoList";
PouchDB.plugin(memory);
let myPouch = null;
beforeEach(() => {
myPouch = new PouchDB("test", { adapter: "memory" });
});
afterEach(async () => {
await myPouch.destroy();
});
test("Test Component", async () => {
// Add needed documents
const putResult = await myPouch.bulkDocs([
{
_id: new Date(2020, 4, 30, 22, 03, 45, 0).toJSON(),
type: "todo",
text: "a todo",
done: false,
},
{
_id: new Date(2020, 4, 30, 21, 03, 45, 0).toJSON(),
type: "todo",
text: "moar todo",
done: false,
},
]);
// Render the Component
const { queryByByText } = render(
<Provider pouchdb={db}>
<TodoList />
</Provider>,
);
const first = queryByByText("moar todo");
expect(first).toBeTruthy();
expect(first.nodeName).toBe("SPAN");
expect(first.parentNode.nodeName).toBe("LI");
expect(first.previousElementSibling.nodeName).toBe("INPUT");
expect(first.previousElementSibling.nodeName.type).toBe("checkbox");
const second = queryByByText("a todo");
expect(second).toBeTruthy();
expect(second.nodeName).toBe("SPAN");
expect(second.parentNode.nodeName).toBe("LI");
expect(second.previousElementSibling.nodeName).toBe("INPUT");
expect(second.previousElementSibling.nodeName.type).toBe("checkbox");
expect(second.parentNode.previousElementSibling).toBe(first.parentNode);
});
myPouch.bulkDocs
is a method to create/update
multiple docs in one go.
Hooks
To test hooks that depend on one of usePouchDB
's hooks, you also must warp it in
<Provider />
.
react-hooks-testing-library's renderHook
function can receive in the second argument a warper
(documented here).
Let's test the addTodo from Add Todos extracted into a hook:
// hooks.js
import { useCallback } from "react";
import { usePouch } from "use-pouchdb";
// This hook returns a function, which we then call with the todo's text.
export function useAddDoc() {
const db = usePouch();
return useCallback(
(text) => {
const doc = {
_id: new Date().toJSON(),
type: "todo",
text: text,
done: false,
};
return db.put(doc);
},
[db],
);
}
import React from "react";
import { renderHook } from "@testing-library/react-hooks";
import PouchDB from "pouchdb";
import memory from "pouchdb-adapter-memory";
import { Provider } from "use-pouchdb";
import { useAddDoc } from "./hooks";
PouchDB.plugin(memory);
let myPouch = null;
beforeEach(() => {
myPouch = new PouchDB("test", { adapter: "memory" });
});
afterEach(async () => {
await myPouch.destroy();
});
test("add document", async () => {
const wrapper = ({ children }) => (
<Provider pouchdb={myPouch}>{children}</Provider>
);
const { result } = renderHook(() => useAddDoc(), {
wrapper,
});
expect(typeof result.current).toBe("function");
await result.current("test todo");
const { rows } = await myPouch.allDocs({ include_docs: true });
expect(rows).toHaveLength(1);
expect(rows[0]).toEqual({
_id: expect.any(String),
_rev: expect.any(String),
type: "todo",
text: "test todo",
done: false,
});
});
Now we are finished with our Todo example. All Todos are replicated, users can sign up and log in. I know, this was a long tutorial, but we did cover a lot!
Happy coding! ๐๐