Encapsulate Test Suites for better readability
parent
d29c16005e
commit
ad73633ae8
|
@ -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);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
|
@ -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);
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue