Skip to content

Commit

Permalink
Merge pull request #7797 from satanTime/issues/7796
Browse files Browse the repository at this point in the history
docs(ActivatedRoute): enhanced description #7796
  • Loading branch information
satanTime committed Dec 25, 2023
2 parents a83f12d + 8891ea1 commit f35b01a
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 28 deletions.
67 changes: 45 additions & 22 deletions docs/articles/guides/mock/activated-route.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ When we are talking about mocking `ActivatedRoute`,
we mean a solution to provide stub params in the snapshot of `ActivatedRoute`
which are used in the component under test.

let's assume we have the next `TargetComponent` component,
## activatedRoute.snapshot

Let's assume we have the next `TargetComponent` component,
which relies on the `paramId` of the `ActivatedRoute` snapshot.

```ts
Expand All @@ -30,35 +32,56 @@ class TargetComponent {
In our test as a mock `ActivatedRoute`, we would like to provide `paramValue` as `paramId`.
To do so, we can use [`MockInstance`](/api/MockInstance.md).

The first step is to call a spy when someone wants to access `snapshot`:
The first step is to call a spy when someone wants to access `snapshot`,
and the second step is to return stub params:

```ts
// for jasmine
MockInstance(ActivatedRoute, 'snapshot', jasmine.createSpy(), 'get');

// for jest
MockInstance(ActivatedRoute, 'snapshot', jest.fn(), 'get');
```

The second step is to return the stub params:

```ts
// for jasmine
MockInstance(ActivatedRoute, 'snapshot', jasmine.createSpy(), 'get')
.and.returnValue({
paramMap: new Map([['paramId', 'paramValue']]),
});
it('works with snapshot from ActivatedRoute', () => {
// for jasmine
// mocking the getter of ActivatedRoute.snapshot
MockInstance(ActivatedRoute, 'snapshot', jasmine.createSpy(), 'get')
// using jasmine.spy.and.returnValue to customize what the getter returns.
.and.returnValue({
paramMap: new Map([['paramId', 'paramValue']]),
});
// for jest
// mocking the getter of ActivatedRoute.snapshot
MockInstance(ActivatedRoute, 'snapshot', jest.fn(), 'get')
// using jest.fn.mockReturnValue to customize what the getter returns.
.mockReturnValue({
paramMap: new Map([['paramId', 'paramValue']]),
});

// for jest
MockInstance(ActivatedRoute, 'snapshot', jest.fn(), 'get')
.mockReturnValue({
paramMap: new Map([['paramId', 'paramValue']]),
});
// the rest of the test
// ...
});
```

Profit. Now, when someone accesses `snapshot` of `ActivatedRoute`, the spy will be called,
which returns a stub `paramMap` with the params we wanted.

## activatedRoute.params

If a component relies on `ActivatedRoute.params` and they should be mocked,
then the approach is very similar to be [above one](#activatedroutesnapshot):

```ts
it('works with params from ActivatedRoute', () => {
// for jasmine
// mocking the getter of ActivatedRoute.snapshot
MockInstance(ActivatedRoute, 'params', jasmine.createSpy(), 'get')
// using jasmine.spy.and.returnValue to customize what the getter returns.
.and.returnValue(of({paramId: 'paramValue'});
// for jest
// mocking the getter of ActivatedRoute.snapshot
MockInstance(ActivatedRoute, 'params', jest.fn(), 'get')
// using jest.fn.mockReturnValue to customize what the getter returns.
.mockReturnValue(of({paramId: 'paramValue'});

// the rest of the test
// ...
});
```
## RouterModule.forRoot
Expand Down
12 changes: 6 additions & 6 deletions libs/ng-mocks/src/lib/mock-builder/mock-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ export type MockBuilderParam = string | AnyDeclaration<any> | NgModuleWithProvid
*/
export function MockBuilder(
keepDeclaration?: MockBuilderParam | MockBuilderParam[] | null | undefined,
itsModuleToMock?: MockBuilderParam | MockBuilderParam[] | null | undefined,
itsModuleAndDependenciesToMock?: MockBuilderParam | MockBuilderParam[] | null | undefined,
): IMockBuilderExtended;

export function MockBuilder(...args: Array<MockBuilderParam | MockBuilderParam[] | null | undefined>): IMockBuilder {
const [keepDeclaration, itsModuleToMock] = args;
const [keep, mock] = args;

const instance = new MockBuilderPerformance(args.length < 2 ? { export: true } : { dependency: true });
const extensions: Map<any, any> = ngMocksUniverse.config.get('MockBuilderExtensions');
Expand All @@ -37,16 +37,16 @@ export function MockBuilder(...args: Array<MockBuilderParam | MockBuilderParam[]
});
}

if (keepDeclaration) {
for (const declaration of flatten(keepDeclaration)) {
if (keep) {
for (const declaration of flatten(keep)) {
instance.keep(declaration, {
export: true,
shallow: isStandalone(declaration),
});
}
}
if (itsModuleToMock) {
for (const declaration of flatten(itsModuleToMock)) {
if (mock) {
for (const declaration of flatten(mock)) {
instance.mock(declaration, declaration, {
export: true,
exportAll: true,
Expand Down
50 changes: 50 additions & 0 deletions tests/issue-7796/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import {
MockBuilder,
MockInstance,
MockRender,
ngMocks,
} from 'ng-mocks';

@Component({
selector: 'route',
template: '{{ params$ | async }}',
})
class RouteComponent implements OnInit {
public params$: Observable<string | undefined> | null = null;

public constructor(private route: ActivatedRoute) {}

ngOnInit() {
this.params$ = this.route.params.pipe(
map(params => params['argle']),
);
}
}

// @see https://github.com/help-me-mom/ng-mocks/issues/7796
describe('issue-7796', () => {
const ofValue = new BehaviorSubject<any>(null);

const of = <T>(value: T): Observable<T> => {
ofValue.next(value);
return ofValue;
};
afterAll(() => {
ofValue.complete();
});

MockInstance.scope();
beforeEach(() => MockBuilder(RouteComponent, [ActivatedRoute]));

it('uses params from ActivatedRoute', async () => {
MockInstance(ActivatedRoute, 'params', of({ argle: 'bargle' }));

const fixture = MockRender(RouteComponent);
expect(ngMocks.formatText(fixture)).toEqual('bargle');
});
});

0 comments on commit f35b01a

Please sign in to comment.