I have a data service that is used by a component.
For example:
BookService:
...
private book: Book;
private bookSubject = new BehaviorSubject<Book>(this.book);
bookChanged = this.bookSubject.asObservable();
...
BookComponent:
...
book: Book;
ngOnInit() {
this.bookService.bookChanged.subscribe(
(book: Book) => this.book = book;
)
}
...
Spec (test file) for the component:
describe('BookComponent', () => {
let component: BookComponent;
let fixture: ComponentFixture<BookComponent>;
let bookServiceStub: Partial<BookService>;
bookServiceStub = {
bookChanged: of({id: 123, name: 'Book 1'})
};
beforeEach(async(() => {
TestBed
.configureTestingModule({
declarations: [BookComponent],
providers: [
{provide: BookService, useValue: bookServiceStub},
...
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BookComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should change the book with a new one', fakeAsync(() => {
const newBook = {
id: 769,
name: 'Book 2'
};
bookServiceStub.bookChanged = of(newBook);
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(component.book.id).toBe(newBook.id); // FAILS (still has old value)
expect(component.book).toBe(newBook); // FAILS (still has old value)
});
}));
});
So the tests fail because the "book" variable does not update with the new values.
What am I doing wrong here?
NOTE: I actually wanted to test if the subscriptions in the component are working as expected or not!
REASON: I would like to check-in further tests that when the value in service is updated, does the DOM changes automatically or not
Here's the order of what happens:
beforeEach(() => {
fixture = TestBed.createComponent(BookComponent);
// The component is created now. So, its constructor is run and services instantiated.
// Also, ngOnInit is executed.
// ...
it('should change the book with a new one', fakeAsync(() => {
const newBook = {
id: 769,
name: 'Book 2'
};
// And now you're overwriting bookServiceStub's bookChanged property.
// The problem is, the component doesn't care at this point, it already has a subscription,
// and it's attached to the original bookChanged stream.
bookServiceStub.bookChanged = of(newBook);
If you don't have more tests to run in this suite (or if you don't need different contents of bookChanged
in them), you can simply move bookServiceStub.bookChanged = of(newBook)
earlier, before the component is created.
Adding component.ngOnInit();
makes my test run successfully.
it('should change the book with a new one', fakeAsync(() => {
const newBook = {
id: 769,
name: 'Book 2'
};
bookServiceStub.bookChanged = of(newBook);
component.ngOnInit(); // ADDED HERE
fixture.detectChanges();
expect(component.book.id).toBe(newBook.id);
expect(component.book).toBe(newBook);
}));
Is this a good practice though. or should I create a spy, etc. ?
It took me a while myself to figure out that fixture.detectChanges(); actually was initiating the data binding of the component. Below should work still, by removing fixture.detectChanges();from beforeEach() and putting it in each test, you are waiting to initiate any data binding to the component.
beforeEach(() => {
fixture = TestBed.createComponent(BookComponent);
component = fixture.componentInstance;
});
it('should create', () => {
fixture.detectChanges();
expect(component).toBeTruthy();
});
it('should change the book with a new one', fakeAsync(() => {
const newBook = {
id: 769,
name: 'Book 2'
};
bookServiceStub.bookChanged = of(newBook);
fixture.detectChanges();
expect(component.book.id).toBe(newBook.id);
expect(component.book).toBe(newBook);
}));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.