Injecting Dependencies and DOM Changes

    quote.component.ts

    This component relies on the QuoteService to get a random quote, which it will then display. The class is pretty simple - it only has the getQuote function that will modify the DOM, therefore it will be our main area of focus in testing.

    In order to test this component we need initiate the QuoteComponent class. The Angular testing library offers a utility called TestBed. This allows us to configure a testing module where we can provided mocked dependencies. Additionally it will create the component for us and return a component fixture that we can perform testing operations on.

    quote.spec.ts

    1. import { QuoteService } from './quote.service';
    2. import { QuoteComponent } from './quote.component';
    3. import { provide, destroyPlatform } from '@angular/core';
    4. import {
    5. async,
    6. inject,
    7. TestBed,
    8. } from '@angular/core/testing';
    9. import {
    10. BrowserDynamicTestingModule,
    11. platformBrowserDynamicTesting
    12. } from '@angular/platform-browser-dynamic/testing';
    13. class MockQuoteService {
    14. public quote: string = 'Test quote';
    15. getQuote() {
    16. return Promise.resolve(this.quote);
    17. }
    18. describe('Testing Quote Component', () => {
    19. beforeEach(() => destroyPlatform());
    20. beforeEach(() => {
    21. TestBed.initTestEnvironment(
    22. BrowserDynamicTestingModule,
    23. platformBrowserDynamicTesting()
    24. );
    25. TestBed.configureTestingModule({
    26. declarations: [
    27. QuoteComponent
    28. ],
    29. { provide: QuoteService, useClass: MockQuoteService }
    30. ]
    31. });
    32. fixture = TestBed.createComponent(QuoteComponent);
    33. fixture.detectChanges();
    34. });
    35. it('Should get quote', async(inject([], () => {
    36. fixture.componentInstance.getQuote();
    37. fixture.whenStable()
    38. .then(() => {
    39. fixture.detectChanges();
    40. return fixture.whenStable();
    41. })
    42. const compiled = fixture.debugElement.nativeElement;
    43. expect(compiled.querySelector('div').innerText).toEqual('Test quote');
    44. });
    45. });

    Testing the QuoteComponent is a fairly straightforward process. We want to create a QuoteComponent, feed it a quote and see if it appears in the DOM. This process requires us to create the component, pass in any dependencies, trigger the component to perform an action and then look at the DOM to see if the action is what we expected.

    Let's take a look at how this is accomplished with the above unit test.

    We use TestBed.initTestingEnvironment to create a testing platform using BrowserDynamicTestingModule and platformBrowserDynamicTesting as arguments, which are also imported from angular and allow the application to be bootstrapped for testing. This is necessary for all unit tests that make use of TestBed. Notice that this platform is destroyed and reset before each test runs.

    We use TestBed.configureTestingModule to feed in any dependencies that our component requires. Here our component depends on the QuoteService to get data. We mock this data ourselves thus giving us control over what value we expect to show up. It is good practice to separate component testing from service testing - this makes it easier to test as you are only focusing on a single aspect of the application at a time. If your service or component fails, how will you know which one was the culprit? We inject the QuoteService dependency using our mock class MockQuoteService, where we will provide mock data for the component to consume.

    In the Should get quote test we have gotten access to our component through the fixture.componentInstance property. We then call getQuote to kickstart our only action in the QuoteComponent component. We run the test when the fixture is stable by using its whenStable method which will ensure the promise inside the getQuote() has resolved, giving the component a chance to set the quote value. We call fixture.detectChanges to keep an eye out for any changes taking place to the DOM, and use the fixture.debugElement.nativeElement property to get access to those underlying DOM elements.

    Now we can check to see if the DOM rendered by our QuoteComponent contains the quote that we mocked in through the QuoteService. The final line attempts to assert that the DOM's div tag contains the mocked quote 'Test Quote' inside. If it does, then our component passes the test and works as expected; if it doesn't, that means our component is not outputting quotes correctly.

    We wrap Should get quote test in async(). This is to allow our tests run in an asynchronous test zone. Using creates a test zone which will ensure that all asynchronous functions have resolved prior to ending the test.