Skip to main content
Early access — new tools and guides added regularly
🟢 Zero to Claude Code — Guide 20 of 22
View track
>_ claude codeBeginner25 min

Testing Your Code with AI Assistance

Learn how to write, run, and interpret tests using AI to generate test cases, catch edge cases, and build confidence in your code.

What you will build
A test suite with unit tests, integration tests, and coverage reporting

Why testing matters and what AI changes

Testing is the safety net that lets you change code without fear. Without tests, every change is a gamble — you might fix one thing and break three others. With tests, you make a change, run the test suite, and know within seconds whether everything still works. Most beginners skip testing because it feels like extra work. AI changes this equation dramatically. Ask Claude Code: Write tests for all the functions in src/utils.ts. In minutes, you have a comprehensive test suite that would have taken hours to write manually. Claude Code understands your functions, generates meaningful test cases, and covers edge cases you might not think of. There are three main types of tests. Unit tests verify individual functions in isolation — does calculateTax(100, 'UK') return 20? Integration tests verify that components work together — does the API endpoint return the right data when called with valid credentials? End-to-end tests verify complete user workflows — can a user sign up, create a post, and see it on their dashboard? Start with unit tests. They are the fastest to write, fastest to run, and easiest to understand. Ask Claude Code: Set up a testing framework for my project. I want Jest with TypeScript support, a test script in package.json, and a sample test file. Run: npm test. You should see Jest discover and run your test file, reporting passes and failures in a clear format.

Setting up Jest and writing your first test

Jest is the most popular JavaScript testing framework. Install it: npm install --save-dev jest ts-jest @types/jest. Create a Jest configuration: npx ts-jest config:init. This creates jest.config.js with TypeScript support. Add a test script to package.json: set test to jest and test:watch to jest --watch. Create your first test file. For a file src/math.ts, create src/math.test.ts (Jest automatically discovers files ending in .test.ts). A basic test looks like: import { add } from './math'; describe('add', () => { it('adds two positive numbers', () => { expect(add(2, 3)).toBe(5); }); it('handles negative numbers', () => { expect(add(-1, 1)).toBe(0); }); it('handles zero', () => { expect(add(0, 0)).toBe(0); }); }). The describe block groups related tests. Each it block is a single test case. The expect function checks that the result matches your expectation. Run: npm test. You should see three passing tests. If any fail, the output shows exactly what was expected versus what was received. Ask Claude Code: Look at my src/math.ts file and write comprehensive tests for every function. Include edge cases like very large numbers, negative numbers, zero, decimal numbers, and invalid inputs. Claude Code generates test cases you would not think of — boundary conditions, type coercion issues, floating point precision problems. Review each test to understand what it is checking and why.

Testing patterns and best practices

Good tests follow the AAA pattern: Arrange, Act, Assert. Arrange sets up the test data and conditions. Act calls the function being tested. Assert checks the result. This structure makes tests readable and maintainable. Ask Claude Code: Refactor my tests to follow the AAA pattern with clear comments separating each phase. Common Jest matchers include: toBe for primitive equality (numbers, strings, booleans), toEqual for deep object equality (comparing objects and arrays), toContain for checking if an array contains a value, toThrow for verifying that a function throws an error, toBeNull, toBeUndefined, toBeTruthy, and toBeFalsy for checking special values. For async code, use async/await in your tests: it('fetches user data', async () => { const user = await getUser(1); expect(user.name).toBe('Alice'); }). Mock external dependencies to keep tests isolated. If your function calls an API, mock the API response: jest.mock('./api', () => ({ fetchUser: jest.fn().mockResolvedValue({ id: 1, name: 'Alice' }) })). This ensures your test does not depend on a real API being available. Ask Claude Code: Identify external dependencies in my functions and create appropriate mocks for testing. I want my tests to run without network access or database connections. Good test names describe the behaviour, not the implementation: prefer 'returns the total price including tax' over 'calls calculateTax and adds to subtotal'. Ask Claude Code: Improve my test names to clearly describe the expected behaviour for each test case.

AI-generated test cases and edge cases

This is where AI-assisted testing truly shines. Humans tend to test the happy path — the normal case where everything works correctly. AI identifies the edge cases that cause production bugs. Ask Claude Code: Review my codebase and identify functions that have no tests. For each one, generate a comprehensive test file that covers normal cases, edge cases, error cases, and boundary conditions. Claude Code will find untested functions, analyse their logic, and generate tests for scenarios like: empty arrays, null or undefined inputs, very long strings, numbers at the maximum safe integer limit, concurrent calls to the same function, Unicode characters in string inputs, timezone-dependent date operations, and race conditions in async code. Run the generated tests: npm test. You will likely discover genuine bugs — functions that crash on empty input, calculations that overflow with large numbers, or string operations that fail with special characters. Ask Claude Code: I have 5 failing tests. For each one, explain whether the test is wrong or the code is wrong. If the code is wrong, fix it. If the test expectation is wrong, fix the test. This iterative cycle — generate tests, find failures, fix code or tests — rapidly improves your code quality. Each bug you find and fix in testing is a bug your users never encounter. For complex business logic, ask Claude Code: Generate property-based test cases for my calculatePricing function. For any valid input combination, the output should always be positive, never exceed the input price, and the discount should never be larger than the original amount. These invariant tests catch bugs that specific test cases miss.

Code coverage and what it means

Code coverage measures how much of your code is executed during testing. Add coverage reporting: update your test script to jest --coverage. Run: npm test. Jest generates a coverage report showing four metrics for each file. Statement coverage: what percentage of statements were executed. Branch coverage: what percentage of if/else branches were taken. Function coverage: what percentage of functions were called. Line coverage: what percentage of lines were executed. You will see a summary table in your terminal and a detailed HTML report in coverage/lcov-report/index.html. Open it in your browser to see exactly which lines are covered (green) and which are not (red). Ask Claude Code: My code coverage is at 65 percent. Identify the uncovered code paths and write tests to cover them. Target 80 percent coverage without writing trivial tests. A common mistake is chasing 100 percent coverage. Some code is not worth testing — simple getters, framework boilerplate, and error logging that just calls console.error. Focus coverage effort on business logic, data transformations, and error handling. Ask Claude Code: Which uncovered lines in my code represent real risk? Prioritise writing tests for those. Add coverage thresholds to jest.config.js: coverageThreshold: { global: { branches: 70, functions: 80, lines: 80, statements: 80 } }. Now npm test fails if coverage drops below these thresholds — preventing coverage regression as you add new code. Common error: if coverage reports show 0 percent for a file, it is probably not imported by any test file. Every file needs at least one test file that imports its exports.

Integration tests and testing API endpoints

Unit tests verify individual functions. Integration tests verify that your components work together correctly — your API endpoint calls the right service, which queries the right database table, which returns the right data. For testing Express or Next.js API routes, use supertest: npm install --save-dev supertest @types/supertest. Create a test file for your API: import request from 'supertest'; import { app } from './app'; describe('GET /api/users', () => { it('returns a list of users', async () => { const response = await request(app).get('/api/users'); expect(response.status).toBe(200); expect(response.body).toHaveLength(3); expect(response.body[0]).toHaveProperty('name'); }); }). Ask Claude Code: Write integration tests for all my API endpoints. Test successful responses, error responses (404, 400, 500), authentication requirements, and input validation. For database-dependent tests, use a test database that resets between tests. Ask Claude Code: Set up a test database configuration that creates a fresh database before each test suite and tears it down after. Use transactions that roll back after each test for speed. Run integration tests separately from unit tests. Add scripts: set test:unit to jest --testPathPattern=unit and test:integration to jest --testPathPattern=integration and test:all to jest. This separation matters because unit tests run in milliseconds while integration tests take seconds. During development, run unit tests on every save (npm run test:unit -- --watch) and integration tests before committing. Ask Claude Code: Organise my tests into unit and integration directories and configure Jest to run them separately. Integration tests catch a different class of bugs than unit tests — misconfigured routes, incorrect database queries, middleware that blocks valid requests, and serialisation issues where the API returns a different shape than the client expects.

Related Lesson

Quality Assurance Fundamentals

This guide is hands-on and practical. The full curriculum covers the conceptual foundations in depth with structured lessons and quizzes.

Go to lesson