Svelte Test Driven Development by Daniel Irvine
Chapters
Notes
Preface
- "It wasn't long before I discovered TDD and how it could help me have a simpler, quieter, calmer life."
Chapter 03 - Loading Data into a Route
- Template for testing SvelteKit server-side page
load
functionality
import { describe, it, expect } from "vitest";
import { load } from "./+page.server.js";
describe("/birthdays - load", () => {
it("returns a fixture of two items", () => {
const result = load();
expect(result).toEqual({
people: [
{ name: "Hercules", dob: "2021-01-01" },
{ name: "Athena", dob: "2021-01-02" },
],
});
}) - [];
});
- Template for testing SvelteKit server-side form
actions
functionality
// mocking form data request object
const createFormDataFromObject = (obj) => {
const formData = new FormData();
Object.entries(obj).forEach(([key, value]) => {
formData.append(key, value);
});
return formData;
};
export const createFormDataRequest = (obj) => ({
formData: () =>
new Promise((resolve) => resolve(createFormDataFromObject(obj))),
});
// assertion on actions object
import { load, actions } from "./+page.server.js";
const request = createFormDataRequest({
name: "Zeus",
dob: "1992-01-01",
});
await actions.default({ request });
expect(load().birthdays).toContainEqual(
expect.objectContaining({ name: "Zeus", dob: "1992-01-01" })
);
- Template for testing SvelteKit component displays
form
prop error messages
describe("validation errors", () => {
it("displays a message", () => {
render(BirthdayForm, {
form: {
error: "An error",
},
});
expect(screen.queryByText("An error")).toBeVisible();
});
});
- Added ability to edit birthday data in addition to creation.
- Changed data structure from a list to a Map.
- Added an edit state to the page component
Chapter 07 - Tidying up Test Suites
- Ways to keep your test suites tidy.
- Application code requires building abstractions and encapsulating details, with deep layers of connecting objects.
- "Test benefit from being shallow, with each test statement having a clear effect.
- Test suites have just one flow
- The primary mechanism you have to control complexity in test suites is abstracting functions that hide detail.
- Hiding necessary but irrelevant data is a key method for keeping unit tests succinct and clear.
- Three Tips:
Page Object Models
for Playwright end-to-end tests
Action helps
for Act
phase of Vitest unit tests
Factories
for Arrange
phase of Vitest unit tests (might be)
Chapter 08 - Creating Matchers to Simply Tests
- Custom Matcher:
toBeUnprocessableEntity
- Does not save data
- Returns error code (e.g. 422)
- Returns error message
- Returns unprocessed values
- Basic structure of a matcher:
export function toTestSomething(received, expected) {
const pass = ...
const message = () => "..."
return {
pass,message
}
}
Negated Matcher
: true
value for pass means expectation failed
- It's important that the custom matcher is defined using the
function
keyword in order for it to gain access to utility function provided via Vite
this.equals
this.utils.diff
this.utils.stringify
this.isNot
- The design of application code should make it easy to write automated unit tests
Chapter 10 - Test-Driving API Endpoints
- Playwright can be used to make HTTP Request via the
request
parameter
test('title', async ({request}) => {
const response = await request.get(url, {data: {...}})
})
- Use
expect.hasAssertions()
to require expect assertions to be called in a catch
of a try-catch block
Chapter 11 - Replacing Behavior with a Side-By-Side Implementation
side-by-side implementation
- is a way to use tests to replace the existing code while ensuring the test suite remains on Green
- In Vitest, a
spy
is created by calling vi.fn
- Incredibly confused by this chapters implementation, but going to brush it off and continue forward
Chapter 12 - Using Component Mocks to Clarify Tests - 15 pages
- The number-one rule when using component mocks, and test doubles in general, is to avoid building any control logic into them.
mockReturnValue
and mockResolvedValue
always return fixed values
- Avoid setting up a test double in a
beforeEach
block
- Hand-rolled component stubs rely on Vitest's
vi.mock
function and __mocks__
directory
JSON.stringify
is a handy method to use in component mocks when you just want to verify that the correct prop structure is passed to a mock from its parent
- BIG NEGATIVE WITH MOCKING COMPONENTS:
- it's challenging to keep the mock aligned with real implementations.
- If needed, use
svelte-component-double
package to mock components and gain access to useful custom matchers: toBeRendered
and toBeRenderedWithProps
- Avoid component mocks if possible because they added complexity to the testing suite
Chapter 13 - Adding Cucumber Tests - 10 pages
- Cucumber tests are not written in JavaScript code. They use a special syntax known as Gherkin within
feature files
- Given, When, Then
- The Gherkin syntax allows for product features to be specified by non-coders in a more human-friendly format, but eventually code re-enters the scene within the step definitions
- Semi-useful to now, but feels just like Playwright E2E tests with more steps.
Chapter 14 - Testing Authentication - 10 pages
Chapter 15 - Test-Driving Svelte Stores - 5 pages
vi.useFakeTimers
and vi
writable([])
- Three things to test:
- Setting the initial value
- Displaying the value (1. On Initial load and 2. On update)
- Updating the value
Chapter 16 - Test-Driving Service Workers - 10 pages