Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
e62ea91
setting up test structure
O-Bots Nov 7, 2025
0add06b
.
O-Bots Nov 7, 2025
ae75bdb
added playwright config file, deleted original playwright folder and …
O-Bots Nov 7, 2025
b3a6b31
continued test structure setup
O-Bots Nov 7, 2025
d91d2af
Updating test folder structure
O-Bots Nov 7, 2025
68011f8
Merge branch 'main' into main
O-Bots Nov 7, 2025
8671300
Merge branch 'CompassConnections:main' into main
O-Bots Nov 8, 2025
b13b8d4
Merge branch 'CompassConnections:main' into main
O-Bots Nov 11, 2025
266a2b4
Added database seeding script and backend testing folder structure
O-Bots Nov 11, 2025
062b6f2
Merge branch 'main' of https://github.com/O-Bots/Obots_Compass
O-Bots Nov 11, 2025
18f24e2
removed the database test
O-Bots Nov 11, 2025
c949891
Replaced db seeding script
O-Bots Nov 11, 2025
dfd5b6f
Updated userInformation.ts to use values from choices.tsx
O-Bots Nov 11, 2025
8bd9f45
merge prep
O-Bots Nov 15, 2025
834c433
Merge branch 'main' of https://github.com/O-Bots/Obots_Compass
O-Bots Nov 15, 2025
7115c22
removing extra unit test, moving api test to correct folder
O-Bots Nov 15, 2025
c039a10
Merge branch 'CompassConnections:main' into main
O-Bots Nov 15, 2025
750d7c9
Pushing to get help with sql Unit test
O-Bots Nov 17, 2025
a7f36c5
Merge branch 'main' of https://github.com/O-Bots/Obots_Compass
O-Bots Nov 17, 2025
e30eac9
Merge branch 'main' into main
O-Bots Nov 17, 2025
f3f2ebf
Updating get-profiles unit tests
O-Bots Nov 19, 2025
48ef836
Added more unit tests
O-Bots Nov 19, 2025
ea7ef9c
.
O-Bots Nov 20, 2025
443996a
Added more unit tests
O-Bots Nov 21, 2025
10f17af
Added getSupabaseToken unit test
O-Bots Nov 21, 2025
a0e48aa
.
O-Bots Nov 21, 2025
f96c122
excluding supabase token test so ci can pass
O-Bots Nov 22, 2025
2a4b002
.
O-Bots Nov 22, 2025
f9bebe3
Seperated the seedDatabase func into its own file so it can be access…
O-Bots Nov 29, 2025
4cd3327
Fixed failing test
O-Bots Nov 29, 2025
25e4d91
.
O-Bots Nov 29, 2025
6f014bb
.
O-Bots Nov 29, 2025
da0a911
Merge branch 'refs/heads/main' into fork/O-Bots/main
MartinBraquet Nov 29, 2025
d658211
Fix tests
MartinBraquet Nov 29, 2025
d76fd2a
Fix lint
MartinBraquet Nov 29, 2025
0359742
Clean
MartinBraquet Nov 29, 2025
8a6f95e
Merge branch 'CompassConnections:main' into main
O-Bots Nov 30, 2025
5ec1aeb
Fixed module paths in compute-score unit test
O-Bots Nov 30, 2025
87c7db0
Updated root tsconfig to recognise backend/shared
O-Bots Nov 30, 2025
9e45f0e
Merge branch 'CompassConnections:main' into main
O-Bots Dec 5, 2025
269f6b2
Added create comment unit test
O-Bots Dec 5, 2025
d7f6d2f
Merge tag '1.7.0'
O-Bots Dec 5, 2025
053aac6
Merge branch 'CompassConnections:main' into main
O-Bots Dec 5, 2025
108ac05
Added some unit tests
O-Bots Dec 11, 2025
581449b
Merge branch 'CompassConnections:main' into main
O-Bots Dec 11, 2025
c8a5d42
Working on createProfile return issue
O-Bots Dec 11, 2025
8668f7c
Merge branch 'main' of https://github.com/O-Bots/Obots_Compass
O-Bots Dec 11, 2025
302aebd
.
O-Bots Dec 11, 2025
d12cbf4
Fixes
MartinBraquet Dec 11, 2025
90e0ed4
Merge branch 'main' into main
O-Bots Dec 11, 2025
bdda4de
Merge branch 'main' into main
MartinBraquet Dec 12, 2025
430e005
Merge branch 'CompassConnections:main' into main
O-Bots Dec 12, 2025
0ddda31
Updated Create profile unit test
O-Bots Dec 14, 2025
7c7a345
Updating create user unit test
O-Bots Dec 21, 2025
83fae05
Add create-user unit tests
O-Bots Dec 27, 2025
866e266
.
O-Bots Dec 28, 2025
e9d73d0
Added more unit tests
O-Bots Jan 3, 2026
f0bcaf3
Merge branch 'CompassConnections:main' into main
O-Bots Jan 4, 2026
cf10408
Added more unit tests
O-Bots Jan 4, 2026
d97001d
.
O-Bots Jan 5, 2026
a7f74fa
Merge branch 'main' into main
O-Bots Jan 5, 2026
77b1f71
Apply suggestion from @MartinBraquet
MartinBraquet Jan 6, 2026
f7dba49
.
O-Bots Jan 6, 2026
de76ce4
Merge branch 'main' of https://github.com/O-Bots/Obots_Compass
O-Bots Jan 6, 2026
e4c877e
Added unit tests
O-Bots Jan 7, 2026
db5bfd0
Added unit tests
O-Bots Jan 7, 2026
904322e
Merge branch 'CompassConnections:main' into main
O-Bots Jan 8, 2026
f8d1bb1
Added unit tests
O-Bots Jan 8, 2026
d751757
Add api unit tests
O-Bots Jan 8, 2026
5a740e2
Added api unit tests | updated set-last-online-time test
O-Bots Jan 10, 2026
d2be1d3
Added api unit tests
O-Bots Jan 11, 2026
c8804b2
Added api unit tests
O-Bots Jan 12, 2026
68e05cd
Updated older unit tests
O-Bots Jan 14, 2026
a18f56c
Updated older unit tests
O-Bots Jan 14, 2026
91846e1
Merge branch 'CompassConnections:main' into main
O-Bots Jan 14, 2026
800a56a
Merge branch 'main' into main
MartinBraquet Jan 15, 2026
49b3d86
Merge branch 'CompassConnections:main' into main
O-Bots Jan 20, 2026
5e9bb26
Adding Unit test documentation
O-Bots Jan 20, 2026
7f144c3
Updating documentation
O-Bots Jan 20, 2026
241ea59
Merge branch 'CompassConnections:main' into main
O-Bots Jan 20, 2026
50a8e05
Added folder structure
O-Bots Jan 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
319 changes: 319 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
## Testing

#### Cover with tests

Best Practices

* Test Behavior, Not Implementation. Don’t test internal state or function calls unless you’re testing utilities or very critical behavior.
* Use msw to Mock APIs. Don't manually mock fetch—use msw to simulate realistic behavior, including network delays and errors.
* Don’t Overuse Snapshots. Snapshots are fragile and often meaningless unless used sparingly (e.g., for JSON response schemas).
* Prefer userEvent Over fireEvent. It simulates real user interactions more accurately.
* Avoid Testing Next.js Internals . You don’t need to test getStaticProps, getServerSideProps themselves-test what they render.
* Don't test just for coverage. Test to prevent regressions, document intent, and handle edge cases.
* Don't write end-to-end tests for features that change frequently unless absolutely necessary.

#### Folder Structure

```filetree
backend/
├── src/
│ ├── controllers/
│ │ └── index.ts
│ └── index.ts
└── test/
├── unit/
│ └── example.unit.test.ts
└── integration/
└── example.integration.test.ts
```

## Jest Unit Testing Guide

### Overview

This guide provides guidlines and best practices for writing unit tests using Jest in this project. Following these standards ensures consistency, maintainability, and comprehensive test coverage.

#### Best Practices

1. Isolate a function route - Each test should focus on one thing that can affect the function outcome
2. Keep tests independent - Tests should not rely on the execution order
3. Use meaningful assertions - Assert that functions are called, what they are called with and the results
4. Avoid testing implementation details - Focus on behavior and outputs
5. Mock external dependencies - Isolate the unit being tested

#### Running Tests
```bash
# Run all tests
yarn test

# Run specific test file
yarn test path/to/test.unit.test.ts
```
#### Test Standards
- Test file names should convey what to expect
- Follow the pattern: "exact-filename.`type of test e.g. unit, integration ect...`.test.ts"
> function-under-test.unit.test.ts
> function-under-test.integration.test.ts
- Group related tests using describe blocks
- Use descriptive test names that explain the expected behavior.
- Follow the pattern: "should `expected behavior` [relevant modifier]"
> should `ban user` [with matching user id]
> should `ban user` [with matching user name]

#### Basic Test Structure

Jest automatically hoists all `jest.mock()` calls to the top of the file before imports are evaluated. To maintain clarity and align with best practices, explicitly place `jest.mock()` calls at the very top of the file.

Modules mocked this way automatically return `undefined`, which is useful for simplifying tests. If a module or function’s return value isn’t used, there’s no need to mock it further.

```tsx
//Function and module mocks
jest.mock('path/to/module');

//Function and module imports
import { functionUnderTest } from "path/to/function"
import { module } from "path/to/module"

describe('functionUnderTest', () => {
//Setup
beforeEach(() => {
//Run before each test
jest.resetAllMocks(); // Resets any mocks from previous tests
});
afterEach(() => {
//Run after each test
jest.restoreAllMocks(); // Cleans up between tests
});

describe('when given valid input', () => {
it('should describe what is being tested', async () => {
//Arrange: Setup test data
const mockData = 'test';

//Act: Execute the function under test
const result = myFunction(mockData);

//Assert: Verify the result
expect(result).toBe('expected');
});
});

describe('when an error occurs', () => {
//Test cases for errors
});
});
```
##### Mocking
Why mocking is important?
- *Isolation* - Test your code independently of databases, APIs, and external systems. Tests only fail when your code breaks, not when a server is down.
- *Speed* - Mocked tests run in milliseconds vs. seconds for real network/database calls. Run your suite constantly without waiting.
- *Control* - Easily simulate edge cases like API errors, timeouts, or rare conditions that are difficult to reproduce with real systems.
- *Reliability* - Eliminate unpredictable failures from network issues, rate limits, or changing external data. Same inputs = same results, every time.
- *Focus* - Verify your function's logic and how it uses its dependencies, without requiring those dependencies to actually work yet.

###### Modules

When mocking modules it's important to verify what was returned if applicable, the amount of times said module was called and what it was called with.

```tsx
//functionFile.ts
import { module } from "path/to/module"

export const functionUnderTest = async (param) => {
return await module(param);
};
---
//testFile.unit.test.ts
jest.mock('path/to/module');

import { functionUnderTest } from "path/to/function"
import { module } from "path/to/module"

/**
* Inside the test case
* We create a mock for any information passed into the function that is being tested
* and if the function returns a result we create a mock to test the result
*/
const mockParam = "mockParam"
const mockReturnValue = "mockModuleValue"

/**
* use .mockResolvedValue when handling async/await modules that return values
* use .mockReturnValue when handling non async/await modules that return values
*/
(module as jest.Mock).mockResolvedValue(mockReturnValue);

const result = await functionUnderTest(mockParam);

expect(result).toBe(mockReturnValue);
expect(module).toBeCalledTimes(1);
expect(module).toBeCalledWith(mockParam);
```
Use namespace imports what you want to import everything a module exports under a single name.

```tsx
//moduleFile.ts
export const module = async (param) => {
const value = "module"
return value
};

export const moduleTwo = async (param) => {
const value = "moduleTwo"
return value
};
```
```tsx
//functionFile.ts
import { module, moduleTwo } from "path/to/module"

export const functionUnderTest = async (param) => {
const mockValue = await moduleTwo(param)
const returnValue = await module(mockValue)
return returnValue;
};
```
```tsx
//testFile.unit.test.ts
jest.mock('path/to/module');

/**
* This creates an object containing all named exports from ./path/to/module
*/
import * as mockModule from "path/to/module"

(mockModule.module as jest.Mock).mockResolvedValue(mockReturnValue);
```
When mocking modules, you can use `jest.spyOn()` instead of `jest.mock()`.

- `jest.mock()` mocks the entire module, which is ideal for external dependencies like Axios or database clients.
- `jest.spyOn()` mocks specific methods while keeping the real implementation for others. It can also be used to observe how a real method is called without changing its behavior.
- also replaces the need to have `jest.mock()` at the top of the file.

```tsx
//testFile.unit.test.ts
import * as mockModule from "path/to/module"

//Mocking the return value of the module
jest.spyOn(mockModule, 'module').mockResolvedValue(mockReturnValue);

//Spying on the module to check functionality
jest.spyOn(mockModule, 'module');

//You can assert the module functionality with both of the above exactly like you would if you used jest.mock()
expect(mockModule.module).toBeCalledTimes(1);
expect(mockModule.module).toBeCalledWith(mockParam);
```
###### Dependencies

Mocking dependencies allows you to test `your code’s` logic in isolation, without relying on third-party services or external functionality.

```tsx
//functionFile.ts
import { dependency } from "path/to/dependency"

export const functionUnderTest = async (param) => {
const depen = await dependency();
const value = depen.module();

return value;
};
```
```tsx
//testFile.unit.test.ts
jest.mock('path/to/dependency');

import { dependency } from "path/to/dependency"

describe('functionUnderTest', () => {
/**
* Because the dependency has modules that are used we need to
* create a variable outside of scope that can be asserted on
*/
let mockDependency = {} as any;
beforeEach(() => {
mockDependency = {
module: jest.fn(),
};
jest.resetAllMocks(); // Resets any mocks from previous tests
});
afterEach(() => {
//Run after each test
jest.restoreAllMocks(); // Cleans up between tests
});

//Inside the test case
(mockDependency.module as jest.Mock).mockResolvedValue(mockReturnValue);

expect(mockDependency.module).toBeCalledTimes(1);
expect(mockDependency.module).toBeCalledWith(mockParam);
});
```
Error checking

```tsx
//function.ts
const result = await functionName(param);

if (!result) {
throw new Error (403, 'Error text', error);
};

---
//testFile.unit.test.ts
const mockParam = {} as any;

//This will check only the error message
expect(functionName(mockParam))
.rejects
.toThrowError('Error text');

---
//This will check the complete error
try {
await functionName(mockParam);
fail('Should have thrown');
} catch (error) {
const functionError = error as Error;
expect(functionError.code).toBe(403);
expect(functionError.nessage).toBe('Error text');
expect(functionError.details).toBe(mockParam);
expect(functionError.name).toBe('Error');
}

---
//For console.error types
console.error('Error message', error);

//Use spyOn to mock
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

expect(errorSpy).toHaveBeenCalledWith(
'Error message',
expect.objectContaining({name: 'Error'}) //The error 'name' refers to the error type
);

```
Mocking array return value

```tsx
//arrayFile.ts
const exampleArray = [ 1, 2, 3, 4, 5 ];

const arrayResult = exampleArray.includes(2);

----
//testFile.unit.test.ts

//This will mock 'includes' for all arrays and force the return value to be true
jest.spyOn(Array.prototype, 'includes').mockReturnValue(true);

---
//This will specify which 'includes' array to mock based on the args passed into the .includes()
jest.spyOn(Array.prototype, 'includes').mockImplementation(function(value) {
if (value === 2) {
return true;
}
return false;
});
```
16 changes: 2 additions & 14 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ See those other useful documents as well:
- [README.md](../backend/api/README.md) for the backend API
- [README.md](../backend/email/README.md) for the email routines and how to set up a local server for quick email rendering
- [README.md](../web/README.md) for the frontend / web server
- [TESTING.md](TESTING.md) for testing guidance and direction

### Adding a new profile field

Expand Down Expand Up @@ -40,17 +41,4 @@ Adding a new language is very easy, especially with translating tools like large
- Duplicate [fr.json](../web/messages/fr.json) and rename it to the locale code (e.g., `de.json` for German). Translate all the strings in the new file (keep the keys identical). LLMs like ChatGPT may not be able to translate the whole file in one go; try to copy-paste by batch of 300 lines and ask the LLM to `translate the values of the json above to <new language> (keep the keys unchanged)`. In order to fit the bottom navigation bar on mobile, make sure the values for those keys are less than 10 characters: "nav.home", "nav.messages", "nav.more", "nav.notifs", "nav.people".
- Duplicate the [fr](../web/public/md/fr) folder and rename it to the locale code (e.g., `de` for German). Translate all the markdown files in the new folder. To do so, you can copy-paste each file into an LLM and ask it to `translate the markdown above to <new language>`.

That's all, no code needed!

### Cover with tests

Best Practices

* Test Behavior, Not Implementation. Don’t test internal state or function calls unless you’re testing utilities or very critical behavior.
* Use msw to Mock APIs. Don't manually mock fetch—use msw to simulate realistic behavior, including network delays and errors.
* Don’t Overuse Snapshots. Snapshots are fragile and often meaningless unless used sparingly (e.g., for JSON response schemas).
* Prefer userEvent Over fireEvent. It simulates real user interactions more accurately.
* Avoid Testing Next.js Internals . You don’t need to test getStaticProps, getServerSideProps themselves—test what they render.
* Use jest.spyOn() for Internal Utilities . Avoid reaching into modules you don’t own.
* Don't test just for coverage. Test to prevent regressions, document intent, and handle edge cases.
* Don't write end-to-end tests for features that change frequently unless absolutely necessary.
That's all, no code needed!
Loading