Skip to content

Commit 8a4270e

Browse files
committed
chore(testing): started the testing docs
1 parent 10cc474 commit 8a4270e

2 files changed

Lines changed: 254 additions & 6 deletions

File tree

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ take up to 60 seconds once the docker build finishes.
1717
### First steps
1818

1919
- Install git commit template: [Commit Template](docs/commit.template.md).
20-
- Volta: [Volta](#volta)
20+
- [Volta](#volta)
2121

2222
### Recommended
2323

24-
- Compodoc: [Compodoc Conventions](docs/compodoc.md).
25-
- Docker Commands: [Docker Commands](docs/docker.md).
26-
- ESLint Strategy: [ESLint Strategy](docs/eslint.md).
27-
- Git Conventions: [Git Conventions](docs/git-convention.md).
28-
- NGXS: [NGXS Conventions](docs/ngxs.md).
24+
- [Compodoc Conventions](docs/compodoc.md).
25+
- [Docker Commands](docs/docker.md).
26+
- [ESLint Strategy](docs/eslint.md).
27+
- [Git Conventions](docs/git-convention.md).
28+
- [NGXS Conventions](docs/ngxs.md).
29+
- [Testing Strategy](docs/testing.md).
2930

3031
### Optional
3132

docs/testing.md

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# OSF Angular Testing Strategy
2+
3+
## Overview
4+
5+
The OSF Angular project uses a modular and mock-driven testing strategy. A shared `testing/` folder provides reusable mocks, mock data, and testing module configuration to support consistent and maintainable unit tests across the codebase.
6+
7+
---
8+
9+
## Index
10+
11+
- [Best Practices](#best-practices)
12+
- [Summary Table](#summary-table)
13+
- [Test Coverage Enforcement (100%)](#test-coverage-enforcement-100)
14+
- [Key Structure](#key-structure)
15+
- [Testing Angular Services (with HTTP)](#testing-angular-services-with-http)
16+
- [Testing Angular Components](#testing-angular-components)
17+
- [Testing Angular Pipes](#testing-angular-pipes)
18+
- [Testing Angular Directives](#testing-angular-directives)
19+
- [Testing Angular NGXS](#testing-ngxs)
20+
21+
--
22+
23+
## Best Practices
24+
25+
- Always import `OsfTestingModule` or `OsfTestingStoreModule` to minimize boilerplate and get consistent mock behavior.
26+
- Use mocks and mock-data from `testing/` to avoid repeating test setup.
27+
- Avoid real HTTP, translation, or store dependencies in unit tests by default.
28+
- Co-locate unit tests with components using `*.spec.ts`.
29+
30+
---
31+
32+
## Summary Table
33+
34+
| Location | Purpose |
35+
| ----------------------- | -------------------------------------- |
36+
| `osf.testing.module.ts` | Unified test module for shared imports |
37+
| `mocks/*.mock.ts` | Mock services and tokens |
38+
| `data/*.data.ts` | Static mock data for test cases |
39+
40+
---
41+
42+
## Test Coverage Enforcement (100%)
43+
44+
This project **strictly enforces 100% test coverage** through the following mechanisms:
45+
46+
### Husky Pre-Push Hook
47+
48+
Before pushing any code, Husky runs a **pre-push hook** that executes:
49+
50+
```bash
51+
npm run test:coverage
52+
```
53+
54+
This command:
55+
56+
- Runs the full test suite with `--coverage`.
57+
- Fails the push if **coverage drops below 100%**.
58+
- Ensures developers never bypass test coverage enforcement locally.
59+
60+
> Pro Tip: Use `npm run test:watch` during development to maintain coverage incrementally.
61+
62+
---
63+
64+
### GitHub Actions CI
65+
66+
Every pull request and push runs GitHub Actions that:
67+
68+
- Run `npm run test:coverage`.
69+
- Verify test suite passes with **100% code coverage**.
70+
- Fail the build if even **1 uncovered branch/line/function** exists.
71+
72+
This guarantees **test integrity in CI** and **prevents regressions**.
73+
74+
---
75+
76+
### Coverage Expectations
77+
78+
| File Type | Coverage Requirement |
79+
| ----------- | ------------------------------------------ |
80+
| `*.ts` | 100% line & branch |
81+
| `*.spec.ts` | Required per file |
82+
| Services | Must mock HTTP via `HttpTestingController` |
83+
| Components | DOM + Input + Output event coverage |
84+
| Pipes/Utils | All edge cases tested |
85+
86+
---
87+
88+
### Summary
89+
90+
- **Zero exceptions** for test coverage.
91+
- **Push blocked** without passing 100% tests.
92+
- GitHub CI double-checks every PR.
93+
94+
## Key Structure
95+
96+
### `testing/osf.testing.module.ts`
97+
98+
This module centralizes commonly used providers, declarations, and test utilities. It's intended to be imported into any `*.spec.ts` test file to avoid repetitive boilerplate.
99+
100+
Example usage:
101+
102+
```ts
103+
import { TestBed } from '@angular/core/testing';
104+
import { OsfTestingModule } from 'testing/osf.testing.module';
105+
106+
beforeEach(async () => {
107+
await TestBed.configureTestingModule({
108+
imports: [OsfTestingModule],
109+
}).compileComponents();
110+
});
111+
```
112+
113+
### OSFTestingModule
114+
115+
**Imports:**
116+
117+
- `NoopAnimationsModule` – disables Angular animations for clean, predictable unit tests.
118+
- `BrowserModule` – required for bootstrapping Angular features.
119+
- `CommonModule` – provides core Angular directives (e.g., `ngIf`, `ngFor`).
120+
- `TranslateModule.forRoot()` – sets up the translation layer for template-based testing with `@ngx-translate`.
121+
122+
**Providers:**
123+
124+
- `provideNoopAnimations()` – disables animation via the new standalone provider API.
125+
- `provideRouter([])` – injects an empty router config for component-level testing.
126+
- `provideHttpClient(withInterceptorsFromDi())` – ensures DI-compatible HTTP interceptors are respected in tests.
127+
- `provideHttpClientTesting()` – injects `HttpTestingController` for mocking HTTP requests in unit tests.
128+
- `TranslationServiceMock` – mocks i18n service methods.
129+
- `EnvironmentTokenMock` – mocks environment config values.
130+
131+
---
132+
133+
### OSFTestingStoreModule
134+
135+
**Imports:**
136+
137+
- `OSFTestingModule` – reuses core mocks and modules.
138+
139+
**Providers:**
140+
141+
- `StoreMock` – mocks NgRx Store for selector and dispatch testing.
142+
- `ToastServiceMock` – injects a mock version of the UI toast service.
143+
144+
### `testing/mocks/`
145+
146+
Provides common service and token mocks to isolate unit tests from real implementations.
147+
148+
**examples**
149+
150+
- `environment.token.mock.ts` – Mocks environment tokens like base API URLs.
151+
- `store.mock.ts` – NGXS or other store-related mocks.
152+
- `translation.service.mock.ts` – Prevents needing actual i18n setup during testing.
153+
- `toast.service.mock.ts` – Mocks user feedback services to track invocations without UI.
154+
155+
---
156+
157+
### `testing/data/`
158+
159+
Includes fake/mock data used by tests to simulate external API responses or internal state.
160+
161+
Only use data from the `testing/data` data mocks to ensure that all data is the centralized.
162+
163+
**examples**
164+
165+
- `addons.authorized-storage.data.ts`
166+
- `addons.external-storage.data.ts`
167+
- `addons.configured.data.ts`
168+
- `addons.operation-invocation.data.ts`
169+
170+
---
171+
172+
---
173+
174+
## Testing Angular Services (with HTTP)
175+
176+
All OSF Angular services that make HTTP requests must be tested using `HttpClientTestingModule` and `HttpTestingController`.
177+
178+
### Setup
179+
180+
```ts
181+
import { HttpTestingController } from '@angular/common/http/testing';
182+
import { OSFTestingModule } from '@testing/osf.testing.module';
183+
184+
let service: YourService;
185+
186+
beforeEach(() => {
187+
TestBed.configureTestingModule({
188+
imports: [OSFTestingModule],
189+
providers: [YourService],
190+
});
191+
192+
service = TestBed.inject(YourService);
193+
});
194+
```
195+
196+
### Example Test
197+
198+
```ts
199+
it('should call correct endpoint and return expected data', inject(
200+
[HttpTestingController],
201+
(httpMock: HttpTestingController) => {
202+
service.getSomething().subscribe((data) => {
203+
expect(data).toEqual(mockData);
204+
});
205+
206+
const req = httpMock.expectOne('/api/endpoint');
207+
expect(req.request.method).toBe('GET');
208+
req.flush(getMockDataFromTestingData());
209+
210+
httpMock.verify(); // Verify no outstanding HTTP calls
211+
}
212+
));
213+
```
214+
215+
### Key Rules
216+
217+
- Use `OSFTestingModule` to isolate the service
218+
- Inject and use `HttpTestingController`
219+
- Always call `httpMock.expectOne()` to verify the URL and method
220+
- Always call `req.flush()` to simulate the backend response
221+
- Add `httpMock.verify()` in each `it` to catch unflushed requests
222+
223+
---
224+
225+
## Testing Angular Components
226+
227+
- coming soon
228+
229+
---
230+
231+
## Testing Angular Pipes
232+
233+
- coming soon
234+
235+
---
236+
237+
## Testing Angular Directives
238+
239+
- coming soon
240+
241+
---
242+
243+
## Testing NGXS
244+
245+
- coming soon
246+
247+
---

0 commit comments

Comments
 (0)