This is Part 3 of Best Practices for Not Getting Lost When Building Real-World Applications

by Richard Voß @richard_voss

Since last year we find ourselves developing web applications for our customers using Angular 4, 5, 6 und now 7. Angular is a popular framework (some call it a platform) for developing web-based front-end applications using Typescript, HTML and CSS. It is open-source and primarily driven by Google.

The framework makes it easier and more fun to structure complex user interfaces, promotes re-use of components and — most valuable — allows test-driven development in the front-end.

Despite all of the greatness of Angular, even with the framework there are lots of mistakes left to make. We have identified a few good patterns and pitfalls that we collected.

This article is part of a series that offers a glimpse into our “Angular Pattern Library”. The last article was about how to make your component look good anywhere, this time we finally get to proper testing techniques.

👍 Mock Components

Unit testing angular components is still Unit Testing.

FooComponent uses a FooService and the component HeadlineAndTextComponente, which uses the PhoneService.

 

Looking at the picture, when unit testing a component, we want to test its component class and template. (The stylesheet is usually not targeted by unit tests but that’s a topic of its own.) But there are a few things attached that we don’t care about.

The Angular Tutorial addresses the problem just very quickly: If a component FooComponent uses another component HeadlineAndTextComponent, then HeadlineAndTextComponent must be part of the TestingModule of FooComponent, otherwise the template won’t compile.

But since the HeadlineAndTextComponent has it’s own unit tests, we do not really want to include it in FooComponent‘s test fixture. The same applies to the FooService, which should be tested in itself. Just like we would mock dependencies of classes in Typescript or Java in order not to double-test things in two places, we want to mock component dependencies. And even if you don’t want to mock everything, you should be able to do it.

 

FooComponente now only uses Mocks of the PhoneService and HeadlineAndTextComponente, the PhoneService is gone.

As the picture shows, the two direct dependencies need to be present, but mocks are sufficient. The PhoneService even becomes irrelevant, because it’s a transitive dependency.

How can I mock a component then?

The solution proposed in the tutorial requires a lot of boilerplate code and potential duplication of the actual component’s interface (inputs/outputs). This is where ng-mocks steps up to the rescue. A little longer around is ng2-mock-component, which is less powerful but helped us to get started.

The following example shows how that would be applied to our scenario above.

describe('FooComponent', () => {
  let fixture: ComponentFixture<FooComponent>;
  let component: FooComponent;

  let fooService: FooService;

  beforeEach(async(() => {
    fooService = mock(FooService);

    when(fooService.getHeadline()).thenReturn('MOCK!');

    TestBed.configureTestingModule({
      declarations: [
        FooComponent, MockComponent(HeadlineAndTextComponent)
      ],
      providers: [
        { provide: FooService, useValue: instance(fooService) }
      ]
    }).compileComponents();
  }));
  beforeEach(() => {
    fixture = TestBed.createComponent(FooComponent);
    component = fixture.debugElement.componentInstance;
    expect(component).toBeTruthy();
    fixture.detectChanges();
  });

  it('passes down the correct headline', () => {
    const subComponent: HeadlineAndTextComponent = fixture.debugElement.query(By.css('headline-and-text')).componentInstance;
    expect(subComponent.headline).toBe('MOCK!');
  });
});

Now that is not very much code but it achieves so much:

  1. it guarantees that HeadlineAndTextComponent is not really instantiated, so any transitive dependencies (services or other components or pipes) are gone
  2. it still produces a component for the TestingModule that has the right selector, inputs and outputs, so any typo in the template would make the test fail
  3. the test actually covers the correct binding of the headline variable into the sub-component, that’s actual application functionality properly tested
  4. the test also proves that the headline text (MOCK!) is obtained from the service

Testing the typescript (class) part of a component is easy, but testing the correct wiring of inputs and outputs in a template is hard to do. With component mocking, things lighten up and become much easier. Try it!

By the way, ng-mocks does the same thing for Pipes and Directives, so all of this applies to them, too.

When should I not mock a component?

It is frequently discussed whether mocking dependencies is always the best thing to do or if sometimes test would be more meaningful if some interactions were not mocked. This question does not only concern Javascript/Typescript or Angular development.

We found the following cases where we would not mock a component but rather include it in the test:

  • when two components are intentionally strictly coupled and the inner component is not supposed to be used without the other:
    • often found for list- and item problems, where special behaviour and layout tuning require them to be coupled
    • the two components should absolutely be in the same module anyway, may be even in a Small Module (we’ll discuss this pattern in a later article)
    • likely, the inner component does not have its own test at all
  • when a component is a third party component (from a library like Angular Material) and the test makes sure it is being used correctly
    • actually, unit tests are a perfect method for trying to find out and documenting how to use a foreign component
    • and they are perfect regression tests when upgrading an external component library

And what about mocking services?

Oh well, that is easy. Services are just objects implementing an interface. Mocking them is nothing special in Angular. We recommend using ts-mockito and you’re done. In the example above, you see it in action for mocking the FooService.

And yes, mocking a service is just as useful as mocking components, because it allows you to focus your tests and not end up tunnel-testing services indirectly.

up next

So far the series was about the good things, our suggestions. Next time I’ll tell a less pleasant tale of a mistake we made and the anti-pattern we’ve concluded from it: 👎 Avoid Component Class Inheritance with Template Duplication.

Seriennavigation<< Angular @ WPS part 2: Making Components Look Good AnywhereAngular @ WPS Part 4: Avoid Component Class Inheritance >>