Skip to content

Commit 15d9fad

Browse files
committed
feat: extract SUT into dedicated files
makes it easier to understand tests
1 parent 52a9782 commit 15d9fad

29 files changed

+456
-629
lines changed

README.md

+34-36
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,36 @@ I've been giving a talk on "Testing with Angular".
1717

1818
All the scenarios are listed here below and nicely linked to the source file.
1919

20-
1. [**Testing Components**](./src/app/components)
21-
* [testing @Input](./src/app/components/input.component.spec.ts)
22-
_Learn how to a component's `@Input()`._
23-
* [testing @Output](./src/app/components/output.component.spec.ts)
24-
_Learn how to test a component's `@Output()`, more specifically the `EventEmitter` type._
25-
* [testing timers inside components](./src/app/components/counter.component.spec.ts)
26-
_Learn about how to handle timers inside components using `fakeAsync` and `discardPeriodicTasks()`._
27-
* [testing DOM manipulations](./src/app/components/domtesting.component.spec.ts)
28-
_Learn how to test the DOM manipulations which might be caused by like `*ngIf` statements etc._
29-
* [testing content projection](./src/app/components/content-projection.component.spec.ts)
30-
_Learn how to test `<ng-content>` directives._
31-
* [testing dynamic CSS classes](./src/app/components/dynamic-css-classes.component.spec.ts)
32-
_Test CSS classes being added and removed dynamically from your component's template based on some conditional expressions._
33-
* [testing dynamic CSS styles](./src/app/components/dynamic-styles.component.spec.ts)
34-
_Test dynamic CSS styles using custom Jasmine matchers._
35-
* [mocking nested components](./src/app/components/nested.component.spec.ts)
36-
_Learn how to mock out nested components which you don't want to necessarily test_
37-
* [async pipes within templates](./src/app/components/async-stream.component.spec.ts)
38-
_Shows how to correctly resolve async pipes and then verify they properly render in the HTML_
39-
1. [**Testing Services**](./src/app/services)
40-
* [Simple stateless function](./src/app/services/greeting.service.spec.ts)
41-
_Learn about different ways of injecting a service into a test case as well as how to test service methods._
42-
* [Async operations](./src/app/services/async.service.spec.ts)
43-
_Learn how to test async operations using the `async()` as well as `fakeAsync()` functions._
44-
* Mocking and remote http calls
45-
_Learn how to mock external dependencies, such as use the `MockBackend` provided by Angular to respond to http calls._
46-
* [Old, < 4.3.1 Http service](./src/app/services/remote.service.spec.ts)
47-
* [New HttpClient from `@angular/common/http`](./src/app/services/remote-new.service.spec.ts)
48-
1. [**Testing Pipes**](./src/app/pipes)
49-
* [custom filter pipe](./src/app/pipes/filter.pipe.spec.ts)
50-
1. [**Custom Matchers and Utilities**](./src/app/utils)
51-
* [Create your own custom Jasmine matchers](./src/app/utils/custom-matchers.ts)
20+
1. [**Testing Components**](./src/app/components)
21+
* [testing @Input](./src/app/components/input.component.spec.ts)
22+
_Learn how to a component's `@Input()`._
23+
* [testing @Output](./src/app/components/output.component.spec.ts)
24+
_Learn how to test a component's `@Output()`, more specifically the `EventEmitter` type._
25+
* [testing timers inside components](./src/app/components/counter.component.spec.ts)
26+
_Learn about how to handle timers inside components using `fakeAsync` and `discardPeriodicTasks()`._
27+
* [testing DOM manipulations](./src/app/components/domtesting.component.spec.ts)
28+
_Learn how to test the DOM manipulations which might be caused by like `*ngIf` statements etc._
29+
* [testing content projection](./src/app/components/content-projection.component.spec.ts)
30+
_Learn how to test `<ng-content>` directives._
31+
* [testing dynamic CSS classes](./src/app/components/dynamic-css-classes.component.spec.ts)
32+
_Test CSS classes being added and removed dynamically from your component's template based on some conditional expressions._
33+
* [testing dynamic CSS styles](./src/app/components/dynamic-styles.component.spec.ts)
34+
_Test dynamic CSS styles using custom Jasmine matchers._
35+
* [mocking nested components](./src/app/components/nested.component.spec.ts)
36+
_Learn how to mock out nested components which you don't want to necessarily test_
37+
* [async pipes within templates](./src/app/components/async-stream.component.spec.ts)
38+
_Shows how to correctly resolve async pipes and then verify they properly render in the HTML_
39+
1. [**Testing Services**](./src/app/services)
40+
* [Simple stateless function](./src/app/services/greeting.service.spec.ts)
41+
_Learn about different ways of injecting a service into a test case as well as how to test service methods._
42+
* [Async operations](./src/app/services/async.service.spec.ts)
43+
_Learn how to test async operations using the `async()` as well as `fakeAsync()` functions._
44+
* [Mocking and remote http calls](./src/app/services/remote.service.spec.ts)
45+
_Learn how to mock external dependencies, such as use the `MockBackend` provided by Angular to respond to http calls._
46+
1. [**Testing Pipes**](./src/app/pipes)
47+
* [custom filter pipe](./src/app/pipes/filter.pipe.spec.ts)
48+
1. [**Custom Matchers and Utilities**](./src/app/utils)
49+
* [Create your own custom Jasmine matchers](./src/app/utils/custom-matchers.ts)
5250

5351
## Articles on Testing
5452

@@ -61,9 +59,9 @@ All the scenarios are listed here below and nicely linked to the source file.
6159

6260
If you want to run the example locally,
6361

64-
1. clone this repository
65-
1. Run `npm install`
66-
1. Run `npm test` to execute all the tests
62+
1. clone this repository
63+
1. Run `npm install`
64+
1. Run `npm test` to execute all the tests
6765

6866
By running `npm run test.watch` you can run the tests in watch mode which is particularly useful during development.
6967

@@ -73,4 +71,4 @@ Wanna help? Of course! Just open an [issue](https://github.com/juristr/angular-t
7371

7472
## Other Links & Resources
7573

76-
- [Testing Angular Material Dialog templates](http://angular-tips.com/blog/2018/02/testing-angular-material-dialog-templates/)
74+
* [Testing Angular Material Dialog templates](http://angular-tips.com/blog/2018/02/testing-angular-material-dialog-templates/)
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,18 @@
1-
import { tick } from '@angular/core/testing';
21
/* tslint:disable:no-unused-variable */
3-
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
43
import { By } from '@angular/platform-browser';
5-
import { DebugElement } from '@angular/core';
6-
7-
import { Component, Output, EventEmitter } from '@angular/core';
8-
import { Observable } from 'rxjs/Observable';
94
import { Subject } from 'rxjs/Subject';
10-
11-
@Component({
12-
selector: 'test',
13-
template: `
14-
<div>{{ personName | async }}</div>
15-
`
16-
})
17-
class AsyncComponent {
18-
personName: Observable<string>;
19-
}
5+
import { AsyncComponent } from './async-stream.component';
206

217
describe('Async Compnent', () => {
228
let component: AsyncComponent;
239
let fixture: ComponentFixture<AsyncComponent>;
2410

25-
beforeEach(
26-
async(() => {
27-
TestBed.configureTestingModule({
28-
declarations: [AsyncComponent]
29-
}).compileComponents();
30-
})
31-
);
11+
beforeEach(async(() => {
12+
TestBed.configureTestingModule({
13+
declarations: [AsyncComponent]
14+
}).compileComponents();
15+
}));
3216

3317
beforeEach(() => {
3418
fixture = TestBed.createComponent(AsyncComponent);
@@ -41,14 +25,18 @@ describe('Async Compnent', () => {
4125
});
4226

4327
it('should correctly visualize the emitted values from the stream', () => {
44-
let stream = new Subject<string>();
28+
const stream = new Subject<string>();
4529
component.personName = stream.asObservable();
4630
fixture.detectChanges();
47-
expect(fixture.debugElement.query(By.css('div')).nativeElement.innerHTML).toBe('');
31+
expect(
32+
fixture.debugElement.query(By.css('div')).nativeElement.innerHTML
33+
).toBe('');
4834

4935
stream.next('Hi');
5036

5137
fixture.detectChanges();
52-
expect(fixture.debugElement.query(By.css('div')).nativeElement.innerHTML).toBe('Hi');
38+
expect(
39+
fixture.debugElement.query(By.css('div')).nativeElement.innerHTML
40+
).toBe('Hi');
5341
});
5442
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Component } from '@angular/core';
2+
import { Observable } from 'rxjs/Observable';
3+
4+
@Component({
5+
selector: 'test',
6+
template: `
7+
<div>{{ personName | async }}</div>
8+
`
9+
})
10+
export class AsyncComponent {
11+
personName: Observable<string>;
12+
}
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,8 @@
11
/* tslint:disable:no-unused-variable */
2-
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3-
import { By } from '@angular/platform-browser';
4-
import { DebugElement } from '@angular/core';
5-
62
import { Component } from '@angular/core';
7-
8-
// the content projected Component
9-
@Component({
10-
selector: 'my-collapsible',
11-
template: `
12-
<h1>
13-
<ng-content select="[title]"></ng-content>
14-
</h1>
15-
<div class="content">
16-
<ng-content select="[body]"></ng-content>
17-
</div>
18-
`
19-
})
20-
class CollapsiblePanel {}
3+
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
4+
import { By } from '@angular/platform-browser';
5+
import { CollapsiblePanelComponent } from './content-projection.component';
216

227
@Component({
238
selector: 'test',
@@ -28,21 +13,20 @@ class CollapsiblePanel {}
2813
</my-collapsible>
2914
`
3015
})
31-
class ContentProjectionComponent {}
16+
class TestComponent {}
3217

3318
describe('ContentProjectionComponent', () => {
34-
let component: ContentProjectionComponent;
35-
let fixture: ComponentFixture<ContentProjectionComponent>;
19+
let component: TestComponent;
20+
let fixture: ComponentFixture<TestComponent>;
3621

3722
beforeEach(async(() => {
3823
TestBed.configureTestingModule({
39-
declarations: [ContentProjectionComponent, CollapsiblePanel]
40-
})
41-
.compileComponents();
24+
declarations: [TestComponent, CollapsiblePanelComponent]
25+
}).compileComponents();
4226
}));
4327

4428
beforeEach(() => {
45-
fixture = TestBed.createComponent(ContentProjectionComponent);
29+
fixture = TestBed.createComponent(TestComponent);
4630
component = fixture.componentInstance;
4731
fixture.detectChanges();
4832
});
@@ -52,11 +36,16 @@ describe('ContentProjectionComponent', () => {
5236
});
5337

5438
it('should correctly project the title', () => {
55-
expect(fixture.debugElement.query(By.css('my-collapsible')).query(By.css('h1')).nativeElement.innerHTML).toContain("I'm the title");
39+
expect(
40+
fixture.debugElement.query(By.css('my-collapsible')).query(By.css('h1'))
41+
.nativeElement.innerHTML
42+
).toContain("I'm the title");
5643
});
5744

5845
it('should correctly project the body', () => {
59-
expect(fixture.debugElement.query(By.css('my-collapsible')).query(By.css('div')).nativeElement.innerHTML).toContain("I'm the body");
60-
});
61-
46+
expect(
47+
fixture.debugElement.query(By.css('my-collapsible')).query(By.css('div'))
48+
.nativeElement.innerHTML
49+
).toContain("I'm the body");
50+
});
6251
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'my-collapsible',
5+
template: `
6+
<h1>
7+
<ng-content select="[title]"></ng-content>
8+
</h1>
9+
<div class="content">
10+
<ng-content select="[body]"></ng-content>
11+
</div>
12+
`
13+
})
14+
export class CollapsiblePanelComponent {}

src/app/components/counter.component.spec.ts

+10-47
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,22 @@
1-
import { Component, OnInit, OnDestroy } from '@angular/core';
2-
31
import {
4-
async,
5-
fakeAsync,
62
ComponentFixture,
7-
tick,
83
TestBed,
9-
discardPeriodicTasks
4+
async,
5+
discardPeriodicTasks,
6+
fakeAsync,
7+
tick
108
} from '@angular/core/testing';
11-
12-
import { By } from '@angular/platform-browser';
13-
14-
@Component({
15-
selector: 'app-counter',
16-
template: ``
17-
})
18-
export class CounterComponent implements OnInit, OnDestroy {
19-
currentCounter = 0;
20-
maxCounter = 10;
21-
private counterInterval;
22-
23-
constructor() {}
24-
25-
ngOnInit() {
26-
this.startCounter();
27-
}
28-
29-
private startCounter() {
30-
this.counterInterval = setInterval(() => {
31-
if (this.currentCounter >= this.maxCounter) {
32-
this.currentCounter = 0;
33-
} else {
34-
this.currentCounter++;
35-
}
36-
}, 1000);
37-
}
38-
39-
ngOnDestroy() {
40-
if (this.counterInterval) {
41-
clearTimeout(this.counterInterval);
42-
}
43-
}
44-
}
9+
import { CounterComponent } from './counter.component';
4510

4611
describe('CounterComponent', () => {
4712
let component: CounterComponent;
4813
let fixture: ComponentFixture<CounterComponent>;
4914

50-
beforeEach(
51-
async(() => {
52-
TestBed.configureTestingModule({
53-
declarations: [CounterComponent]
54-
}).compileComponents();
55-
})
56-
);
15+
beforeEach(async(() => {
16+
TestBed.configureTestingModule({
17+
declarations: [CounterComponent]
18+
}).compileComponents();
19+
}));
5720

5821
beforeEach(() => {
5922
fixture = TestBed.createComponent(CounterComponent);
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Component, OnInit, OnDestroy } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-counter',
5+
template: ``
6+
})
7+
export class CounterComponent implements OnInit, OnDestroy {
8+
currentCounter = 0;
9+
maxCounter = 10;
10+
private counterInterval;
11+
12+
constructor() {}
13+
14+
ngOnInit() {
15+
this.startCounter();
16+
}
17+
18+
private startCounter() {
19+
this.counterInterval = setInterval(() => {
20+
if (this.currentCounter >= this.maxCounter) {
21+
this.currentCounter = 0;
22+
} else {
23+
this.currentCounter++;
24+
}
25+
}, 1000);
26+
}
27+
28+
ngOnDestroy() {
29+
if (this.counterInterval) {
30+
clearTimeout(this.counterInterval);
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)