Skip to content

Commit

Permalink
docs: examples with mock pipes
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Dec 5, 2020
1 parent 8bd56d0 commit 1061498
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 47 deletions.
94 changes: 61 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
[pipes](#how-to-create-a-mock-pipe),
[services](#how-to-create-a-mock-provider) and
[modules](#how-to-create-a-mock-module)
in tests of Angular 5+ applications.
Whether you need a [mock child component](#how-to-create-a-mock-component),
in tests for Angular 5+ applications.
When you need a [mock child component](#how-to-create-a-mock-component),
or any other [annoying dependency](#how-to-turn-annoying-declarations-into-mocks-in-an-angular-application),
`ng-mocks` has tools to turn these declarations into their mocks,
keeping interfaces as they are, but suppressing their implementation.
Expand Down Expand Up @@ -307,7 +307,8 @@ of all aspects might be useful in writing fully isolated unit tests.

### How to create a mock component

There is a `MockComponent` function. It covers almost all needs for mock behavior.
There is a `MockComponent` function.
It covers everything you need to turn a component into its mock declaration.

- `MockComponent( MyComponent )` - returns a mock class of `MyComponent` component.
- `MockComponents( MyComponent1, SomeComponent2, ... )` - returns an array of mocks.
Expand Down Expand Up @@ -425,8 +426,7 @@ describe('MockComponent', () => {
// By.directive(DependencyComponent)
// ).componentInstance
// but properly typed.
const mockComponent = ngMocks.find(DependencyComponent)
.componentInstance;
const mockComponent = ngMocks.findInstance(DependencyComponent);

// Again, let's pretend DependencyComponent has an output
// called 'someOutput'. TestedComponent listens on the output via
Expand Down Expand Up @@ -497,7 +497,8 @@ describe('MockComponent', () => {

### How to create a mock directive

There is a `MockDirective` function covering almost all needs for mock behavior.
There is a `MockDirective` function.
It turns a directive into its mock declaration.

- `MockDirective( MyDirective )` - returns a mock class of `MyDirective` directive.
- `MockDirectives( MyDirective1, MyDirective2, ... )` - returns an array of mocks.
Expand Down Expand Up @@ -690,10 +691,10 @@ describe('MockDirective:Structural', () => {

### How to create a mock pipe

`MockPipe` is a function that creates mock pipes for needs in Angular testing.
`ngmocks` has a `MockPipe` function that creates mock pipes with an empty or a custom handler.

- `MockPipe( MyPipe )` - returns a mock class of `MyPipe` pipe that always transforms to `undefined`.
- `MockPipe( MyPipe, value => 'stub behavior' )` - returns a mock class of `MyPipe` pipe.
- `MockPipe( MyPipe, value => 'fake' )` - returns a mock class of `MyPipe` pipe that transforms to `fake`.
- `MockPipes( MyPipe1, MyPipe2, ... )` - returns an array of mocks.

**A mock pipe** respects the interface of its original pipe as
Expand Down Expand Up @@ -733,7 +734,7 @@ To **create a mock pipe** simply pass its class into `MockPipe`:
TestBed.configureTestingModule({
declarations: [
TargetComponent,
MockPipe(DependencyPipe), // <- profit
MockPipe(DependencyPipe, value => `mock:${value}`), // <- profit
],
});
```
Expand All @@ -749,6 +750,11 @@ describe('Test', () => {
it('should create', () => {
const fixture = MockRender(TargetComponent);
expect(fixture.point.componentInstance).toBeDefined();
expect(fixture.nativeElement.innerHTML).toContain('mock:foo');

// An instance of DependencyPipe from the fixture if we need it.
const pipe = ngMocks.findInstance(DependencyPipe);
expect(pipe).toBeDefined();
});
});
```
Expand All @@ -764,11 +770,19 @@ to play with.

```typescript
describe('MockPipe', () => {
// A fake transform function.
const fakeTransform = (...args: string[]) => JSON.stringify(args);

// A spy, just in case if we want to verify
// how the pipe has been called.
const spy = jasmine
.createSpy('transform')
.and.callFake(fakeTransform);
// in case of jest
// const spy = jest.fn().mockImplementation(fakeTransform);

beforeEach(() => {
return MockBuilder(TestedComponent).mock(
DependencyPipe,
(...args: string[]) => JSON.stringify(args),
);
return MockBuilder(TestedComponent).mock(DependencyPipe, spy);
});

it('transforms values to json', () => {
Expand All @@ -777,6 +791,12 @@ describe('MockPipe', () => {
expect(fixture.nativeElement.innerHTML).toEqual(
'<component>["foo"]</component>',
);

// Also we can find an instance of the pipe in
// the fixture if it's needed.
const pipe = ngMocks.findInstance(DependencyPipe);
expect(pipe.transform).toHaveBeenCalledWith('foo');
expect(pipe.transform).toHaveBeenCalledTimes(1);
});
});
```
Expand Down Expand Up @@ -1392,19 +1412,16 @@ Prefix it with `fdescribe` or `fit` on
to play with.

```typescript
import { CommonModule } from '@angular/common';
import {
Component,
ContentChild,
ElementRef,
EventEmitter,
Input,
NgModule,
Output,
TemplateRef,
} from '@angular/core';
import { RouterModule } from '@angular/router';
import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';
@Pipe({
name: 'translate',
})
class TranslatePipe implements PipeTransform {
public transform(value: string): string {
// Just for the test purpose
// we don't use any translation services.
return `translated:${value}`;
}
}

// Our main component that we want to test.
@Component({
Expand All @@ -1417,8 +1434,12 @@ import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';
>
<ng-template #menu>
<ul>
<li><a [routerLink]="['/home']">Home</a></li>
<li><a [routerLink]="['/about']">Home</a></li>
<li>
<a [routerLink]="['/home']">{{ 'Home' | translate }}</a>
</li>
<li>
<a [routerLink]="['/about']">{{ 'About' | translate }}</a>
</li>
</ul>
</ng-template>
</app-header>
Expand Down Expand Up @@ -1451,7 +1472,7 @@ class AppHeaderComponent {

// The module where our components are declared.
@NgModule({
declarations: [AppComponent, AppHeaderComponent],
declarations: [AppComponent, AppHeaderComponent, TranslatePipe],
imports: [CommonModule, RouterModule.forRoot([])],
})
class AppModule {}
Expand Down Expand Up @@ -1488,6 +1509,8 @@ describe('main', () => {
menu: true,
},
})
// a fake transform handler.
.mock(TranslatePipe, v => `fake:${v}`)
);
// the same as
// TestBed.configureTestingModule({
Expand Down Expand Up @@ -1557,11 +1580,16 @@ describe('main', () => {
// AppHeaderComponent.
const links = ngMocks.findAll(header, 'a');
expect(links.length).toBe(2);
const [link1, link2] = links;

// Checking that TranslatePipe has been used.
expect(link1.nativeElement.innerHTML).toEqual('fake:Home');
// An easy way to get a value of an input. The same as
// links[0].injector.get(RouterLinkWithHref).routerLink
expect(ngMocks.input(links[0], 'routerLink')).toEqual(['/home']);
expect(ngMocks.input(links[1], 'routerLink')).toEqual(['/about']);
expect(ngMocks.input(link1, 'routerLink')).toEqual(['/home']);

expect(link2.nativeElement.innerHTML).toEqual('fake:About');
expect(ngMocks.input(link2, 'routerLink')).toEqual(['/about']);
});
});
```
Expand Down Expand Up @@ -1834,7 +1862,7 @@ beforeEach(() => {
By default, when [`.mock(MyService, mock)`](#mockbuildermock) is used it creates a mock object via
[`MockService(MyService, mock)`](#how-to-create-a-mock-service).
In some cases we might want to use the exactly passed mock object instead of extension.
For this behavior we need to set `precise` flag to `true`.
For this behavior we need to set `precise` flag to `true`. Tokens are always precise.

```typescript
declare class MyService {
Expand Down Expand Up @@ -2832,7 +2860,7 @@ describe('beforeEach:manual-spy', () => {

### Auto Spy

If you want **automatically spy all functions in Angular tests** then
If you want **automatically to spy all methods of components, directives, pipes and services in Angular tests** then
add the next code to `src/test.ts`.

```typescript
Expand Down
3 changes: 3 additions & 0 deletions e2e/a10/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,8 @@
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "^3.9.6"
},
"renovate": {
"enabled": false
}
}
3 changes: 3 additions & 0 deletions e2e/a11/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,8 @@
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.0.2"
},
"renovate": {
"enabled": false
}
}
3 changes: 3 additions & 0 deletions e2e/a5es2015/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,8 @@
"testPathIgnorePatterns": [
"<rootDir>/src/test.ts"
]
},
"renovate": {
"enabled": false
}
}
3 changes: 3 additions & 0 deletions e2e/a5es5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,8 @@
"testPathIgnorePatterns": [
"<rootDir>/src/test.ts"
]
},
"renovate": {
"enabled": false
}
}
3 changes: 3 additions & 0 deletions e2e/a6/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,8 @@
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~2.9.2"
},
"renovate": {
"enabled": false
}
}
3 changes: 3 additions & 0 deletions e2e/a7/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,8 @@
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.2.2"
},
"renovate": {
"enabled": false
}
}
3 changes: 3 additions & 0 deletions e2e/a8/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,8 @@
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3"
},
"renovate": {
"enabled": false
}
}
3 changes: 3 additions & 0 deletions e2e/a9/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,8 @@
"ts-node": "~8.3.0",
"tslint": "~5.18.0",
"typescript": "~3.7.5"
},
"renovate": {
"enabled": false
}
}
3 changes: 1 addition & 2 deletions examples/MockComponent/test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ describe('MockComponent', () => {
// By.directive(DependencyComponent)
// ).componentInstance
// but properly typed.
const mockComponent = ngMocks.find(DependencyComponent)
.componentInstance;
const mockComponent = ngMocks.findInstance(DependencyComponent);

// Again, let's pretend DependencyComponent has an output
// called 'someOutput'. TestedComponent listens on the output via
Expand Down
24 changes: 19 additions & 5 deletions examples/MockPipe/test.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, Pipe, PipeTransform } from '@angular/core';
import { MockBuilder, MockRender } from 'ng-mocks';
import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';

@Pipe({ name: 'dependency' })
class DependencyPipe implements PipeTransform {
Expand All @@ -13,11 +13,19 @@ class DependencyPipe implements PipeTransform {
class TestedComponent {}

describe('MockPipe', () => {
// A fake transform function.
const fakeTransform = (...args: string[]) => JSON.stringify(args);

// A spy, just in case if we want to verify
// how the pipe has been called.
const spy = jasmine
.createSpy('transform')
.and.callFake(fakeTransform);
// in case of jest
// const spy = jest.fn().mockImplementation(fakeTransform);

beforeEach(() => {
return MockBuilder(TestedComponent).mock(
DependencyPipe,
(...args: string[]) => JSON.stringify(args),
);
return MockBuilder(TestedComponent).mock(DependencyPipe, spy);
});

it('transforms values to json', () => {
Expand All @@ -26,5 +34,11 @@ describe('MockPipe', () => {
expect(fixture.nativeElement.innerHTML).toEqual(
'<component>["foo"]</component>',
);

// Also we can find an instance of the pipe in
// the fixture if it's needed.
const pipe = ngMocks.findInstance(DependencyPipe);
expect(pipe.transform).toHaveBeenCalledWith('foo');
expect(pipe.transform).toHaveBeenCalledTimes(1);
});
});
Loading

0 comments on commit 1061498

Please sign in to comment.