Encapsulate Test Suites for better readability

merge-requests/1/head
Florian Hartwich 2017-04-12 19:37:52 +02:00
parent d29c16005e
commit ad73633ae8
6 changed files with 157 additions and 149 deletions

View File

@ -6,79 +6,84 @@ import {RouterTestingModule} from "@angular/router/testing";
describe('Blog Entry Isolated Test', () => { describe('Blog Entry Isolated Test', () => {
it('should render DOM correctly according to Input', () => { describe('Isolated Test', () => {
// Umgebung initialisieren it('should render DOM correctly according to Input', () => {
TestBed.configureTestingModule({ // Umgebung initialisieren
declarations: [BlogEntryComponent] TestBed.configureTestingModule({
declarations: [BlogEntryComponent]
});
const fixture = TestBed.createComponent(BlogEntryComponent);
const blogEntryComponent: BlogEntryComponent = fixture.componentInstance;
const element = fixture.nativeElement;
const blogEntry: BlogEntry = new BlogEntry;
// Testdaten
const testId = 101;
const testTitle = "test title";
const testText = "test text";
const testImage = "https://www.google.de/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png";
// Input-Daten
blogEntry.id = testId;
blogEntry.title = testTitle;
blogEntry.text = testText;
blogEntry.image = testImage;
blogEntry.createdAt = new Date();
(<any> blogEntryComponent).entry = blogEntry;
// DOM update
fixture.detectChanges();
// DOM auslesen und Daten abgleichen
const imageSrc = element.querySelector('div /deep/ .blog-image > img').getAttribute("src");
expect(imageSrc).toBe(testImage);
// textContent statt innerHtml
// siehe http://stackoverflow.com/questions/40227533/angular-2-and-jasmine-unit-testing-cannot-get-the-innerhtml
const blogTitle = element.querySelector('div /deep/ .blog-summary > span').textContent;
expect(blogTitle).toBe(testTitle);
const blogText = element.querySelector('div /deep/ .blog-summary > p').textContent;
expect(blogText).toBe(testText);
const blogTs = element.querySelector('div /deep/ .blog-timestamp > .timestamp').textContent;
expect(new Date(blogTs).getDate()).toBe(new Date().getDate());
const blogDelete = element.querySelector('div /deep/ .blog-delete > button').textContent;
expect(blogDelete).toBe("Entfernen")
// keine Überprüfung der id, die lediglich im EventListener des Delete Button hinterlegt ist, möglich!
// nur durch click-Event auslösen, was jedoch BlogComponent vorraussetzt
}); });
const fixture = TestBed.createComponent(BlogEntryComponent); });
const blogEntryComponent: BlogEntryComponent = fixture.componentInstance;
const element = fixture.nativeElement;
const blogEntry: BlogEntry = new BlogEntry;
// Testdaten
const testId = 101;
const testTitle = "test title";
const testText = "test text";
const testImage = "https://www.google.de/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png";
// Input-Daten describe('BlogEntryComp -> BlogComp Dependent Test', () => {
blogEntry.id = testId;
blogEntry.title = testTitle;
blogEntry.text = testText;
blogEntry.image = testImage;
blogEntry.createdAt = new Date();
(<any> blogEntryComponent).entry = blogEntry;
// DOM update it('should remove entry from BlogComponent on "delete" button click', () => {
fixture.detectChanges(); TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([])],
declarations: [BlogEntryComponent, BlogComponent]
});
// DOM auslesen und Daten abgleichen const entryFixture = TestBed.createComponent(BlogEntryComponent);
const imageSrc = element.querySelector('div /deep/ .blog-image > img').getAttribute("src"); const entryInstance: BlogEntryComponent = entryFixture.componentInstance;
expect(imageSrc).toBe(testImage);
// textContent statt innerHtml const blogFixture = TestBed.createComponent(BlogComponent);
// siehe http://stackoverflow.com/questions/40227533/angular-2-and-jasmine-unit-testing-cannot-get-the-innerhtml const blogInstance: BlogComponent = blogFixture.componentInstance;
const blogTitle = element.querySelector('div /deep/ .blog-summary > span').textContent;
expect(blogTitle).toBe(testTitle);
const blogText = element.querySelector('div /deep/ .blog-summary > p').textContent; const blogEntry: BlogEntry = blogInstance.entries[0];
expect(blogText).toBe(testText); entryInstance.entry = blogEntry;
entryInstance.blogComponent = blogInstance;
const blogTs = element.querySelector('div /deep/ .blog-timestamp > .timestamp').textContent; const oldLength = blogInstance.entries.length;
expect(new Date(blogTs).getDate()).toBe(new Date().getDate()); entryFixture.nativeElement.querySelector('div /deep/ .blog-delete > button').click();
expect(blogInstance.entries.length).toBe(oldLength - 1);
const blogDelete = element.querySelector('div /deep/ .blog-delete > button').textContent; });
expect(blogDelete).toBe("Entfernen")
// keine Überprüfung der id, die lediglich im EventListener des Delete Button hinterlegt ist, möglich!
// nur durch click-Event auslösen, was jedoch BlogComponent vorraussetzt
}); });
}); });
describe('BlogEntryComp -> BlogComp Dependent Test', () => {
it('should remove entry from BlogComponent on "delete" button click', () => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([])],
declarations: [BlogEntryComponent, BlogComponent]
});
const entryFixture = TestBed.createComponent(BlogEntryComponent);
const entryInstance: BlogEntryComponent = entryFixture.componentInstance;
const blogFixture = TestBed.createComponent(BlogComponent);
const blogInstance: BlogComponent = blogFixture.componentInstance;
const blogEntry: BlogEntry = blogInstance.entries[0];
entryInstance.entry = blogEntry;
entryInstance.blogComponent = blogInstance;
const oldLength = blogInstance.entries.length;
entryFixture.nativeElement.querySelector('div /deep/ .blog-delete > button').click();
expect(blogInstance.entries.length).toBe(oldLength - 1);
});
});

View File

@ -2,86 +2,88 @@ import {BlogComponent} from './blog.component'
import {TestBed} from "@angular/core/testing"; import {TestBed} from "@angular/core/testing";
import {RouterTestingModule} from "@angular/router/testing"; import {RouterTestingModule} from "@angular/router/testing";
import {BlogEntryComponent} from "./blog-entry/blog-entry.component"; import {BlogEntryComponent} from "./blog-entry/blog-entry.component";
import {BlogEntry} from "./blog-entry/blog-entry";
describe('Blog Component Isolated Test', () => { describe('Blog Component', () => {
let blogComponent: BlogComponent; describe('Isolated Class Test', () => {
let blogComponent: BlogComponent;
beforeEach(() => { beforeEach(() => {
blogComponent = new BlogComponent(null, null, null); blogComponent = new BlogComponent(null, null, null);
});
it('should have initial entries', () => {
expect(blogComponent.entries.length).toBe(2);
blogComponent.entries.forEach((entry) => {
expect(entry.id).toBeLessThanOrEqual(blogComponent.id);
expect(entry.createdAt.getDate()).toBe(new Date().getDate());
})
});
it('should create new list entry and increment id', () => {
let preCreationId = blogComponent.id;
let entryTitle = "some fancy title";
let entryImage = "https://avatars1.githubusercontent.com/u/3284117";
let entryText = "some important text";
blogComponent.createBlogEntry(entryTitle, entryImage, entryText);
let newEntry = blogComponent.entries[blogComponent.entries.length - 1];
expect(newEntry.id - 1).toBe(preCreationId);
expect(newEntry.image).toBe(entryImage);
expect(newEntry.text).toBe(entryText);
expect(newEntry.createdAt.getDate()).toBe(new Date().getDate());
});
it('should delete entry by given id - and not change global max-id', () => {
let preDeletionId = blogComponent.id;
let latestId = blogComponent.entries[blogComponent.entries.length - 1].id;
blogComponent.deleteBlogEntry(latestId);
expect(blogComponent.id).toBe(preDeletionId);
expect(() => {
if (blogComponent.entries.length > 0) {
return blogComponent.entries[blogComponent.entries.length - 1];
} else {
return 0;
}
}).not.toBe(latestId);
});
});
describe('Blog Component Integration Form Test', () => {
let fixture;
let instance;
let element;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([])],
declarations: [BlogEntryComponent, BlogComponent]
}); });
fixture = TestBed.createComponent(BlogComponent);
instance = fixture.componentInstance;
element = fixture.nativeElement;
});
it('should call create method with provided field input', () => { it('should have initial entries', () => {
const spy = spyOn(instance, 'createBlogEntry'); expect(blogComponent.entries.length).toBe(2);
blogComponent.entries.forEach((entry) => {
expect(entry.id).toBeLessThanOrEqual(blogComponent.id);
expect(entry.createdAt.getDate()).toBe(new Date().getDate());
})
});
const testTitle = 'testTitle'; it('should create new list entry and increment id-pointer', () => {
const testImage = 'imageUrl'; let preCreationId = blogComponent.id;
const testText = 'testText'; let entryTitle = "some fancy title";
let entryImage = "https://avatars1.githubusercontent.com/u/3284117";
let entryText = "some important text";
blogComponent.createBlogEntry(entryTitle, entryImage, entryText);
// beachten : value würde immer funktionieren um Werte als Variable let newEntry = blogComponent.entries[blogComponent.entries.length - 1];
// übertragbar einzutragen, textContent hingegen nur bei textarea expect(newEntry.id - 1).toBe(preCreationId);
element.querySelector('div /deep/ div > #title').value = testTitle; expect(newEntry.image).toBe(entryImage);
element.querySelector('div /deep/ div > #image').value = testImage; expect(newEntry.text).toBe(entryText);
element.querySelector('div /deep/ div > #text').textContent = testText; expect(newEntry.createdAt.getDate()).toBe(new Date().getDate());
element.querySelector('div /deep/ div > button').click(); });
expect(spy).toHaveBeenCalledWith(testTitle, testImage, testText); it('should delete entry by given id - and not change global max-id', () => {
}) let preDeletionId = blogComponent.id;
let latestId = blogComponent.entries[blogComponent.entries.length - 1].id;
blogComponent.deleteBlogEntry(latestId);
expect(blogComponent.id).toBe(preDeletionId);
expect(() => {
if (blogComponent.entries.length > 0) {
return blogComponent.entries[blogComponent.entries.length - 1];
} else {
return 0;
}
}).not.toBe(latestId);
});
});
describe('Template Driven Form Integration Test', () => {
let fixture;
let instance;
let element;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([])],
declarations: [BlogEntryComponent, BlogComponent]
});
fixture = TestBed.createComponent(BlogComponent);
instance = fixture.componentInstance;
element = fixture.nativeElement;
});
it('should call create method with provided field input', () => {
const spy = spyOn(instance, 'createBlogEntry');
const testTitle = 'testTitle';
const testImage = 'imageUrl';
const testText = 'testText';
// beachten : value würde immer funktionieren um Werte als Variable
// übertragbar einzutragen, textContent hingegen nur bei textarea
element.querySelector('div /deep/ div > #title').value = testTitle;
element.querySelector('div /deep/ div > #image').value = testImage;
element.querySelector('div /deep/ div > #text').textContent = testText;
element.querySelector('div /deep/ div > button').click();
expect(spy).toHaveBeenCalledWith(testTitle, testImage, testText);
})
});
}); });

View File

@ -32,6 +32,7 @@ export class LoginService {
return this.results$; return this.results$;
} }
// Nur als theoretische Umsetzung - in Realität findet die PW-Validierung serverseitig statt
login(name, password) : boolean { login(name, password) : boolean {
if (this.getUser(name)) { if (this.getUser(name)) {
let user = this.results$[0]; let user = this.results$[0];

View File

@ -38,7 +38,7 @@ describe('Login-Service', () => {
}) })
); );
it('should trigger a HTTP-GET and receive Task', (() => { it('should trigger a HTTP-GET and receive Users', (() => {
mockBackend.connections.subscribe(connection => { mockBackend.connections.subscribe(connection => {
const expectedUrl = 'http://localhost:3000/api/users/'; const expectedUrl = 'http://localhost:3000/api/users/';
expect(connection.request.url).toBe(expectedUrl); expect(connection.request.url).toBe(expectedUrl);

View File

@ -13,24 +13,24 @@ export class UserStore {
this.items$.next(this.users); this.items$.next(this.users);
} }
_reduce(tasks: User[], action) { _reduce(users: User[], action) {
switch (action.type) { switch (action.type) {
case LOAD: case LOAD:
return [...action.data]; return [...action.data];
case ADD: case ADD:
return [...tasks, action.data]; return [...users, action.data];
case EDIT: case EDIT:
return tasks.map(task => { return users.map(user => {
const editedTask = action.data; const editedUser = action.data;
if (task.id !== editedTask.id) { if (user.id !== editedUser.id) {
return task; return user;
} }
return editedTask; return editedUser;
}); });
case REMOVE: case REMOVE:
return tasks.filter(task => task.id !== action.data.id); return users.filter(task => task.id !== action.data.id);
default: default:
return tasks; return users;
} }
} }

View File

@ -40,7 +40,7 @@ describe('EditTask Component', () => {
}); });
describe('Template Driven Form API-based Test', ()=>{ describe('Template Driven Form API-based Test', () => {
let fixture; let fixture;
@ -51,7 +51,7 @@ describe('EditTask Component', () => {
fixture.autoDetectChanges(true); fixture.autoDetectChanges(true);
}); });
it('should validate the title correctly', (done)=> { it('should validate the title correctly', (done) => {
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
form = fixture.componentInstance.form.form; form = fixture.componentInstance.form.form;
const titleControl = form.get('title'); const titleControl = form.get('title');
@ -103,7 +103,7 @@ describe('EditTask Component', () => {
}); });
}); });
it('should show error on tag name provided with gt 0 and lt 3', (done) => { it('should validate assignee email correctly', (done) => {
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
form = fixture.componentInstance.form.form; form = fixture.componentInstance.form.form;
const invalidEmailObject = {invalidEMail: true}; const invalidEmailObject = {invalidEMail: true};
@ -127,7 +127,7 @@ describe('EditTask Component', () => {
}); });
describe('Routing', ()=>{ describe('Routing', () => {
let taskService: TaskService; let taskService: TaskService;