---
title: 3 Reasons You Should Test Your Angular Component Templates
description: Learn why it is important to unit test the template of your Angular components and how it can help catch errors and ensure the correct functionality.
image: https://www.bitovi.com/hubfs/3%20Reasons%20You%20Should%20Test%20Your%20Angular%20Component%20Templates-1.png
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting)
- ![Project Management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project Management](https://www.bitovi.com/services/agile-project-management-consulting)
- ![Product Design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product Design](https://www.bitovi.com/services/product-design-consulting)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting)
- [Design systems](https://www.bitovi.com/services/axure-figma-migration)
- [React](https://www.bitovi.com/services/frontend/react-consulting)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about delivering products and solving problems.
  
  ](https://www.bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships)
- [![Academy](https://www.bitovi.com/hubfs/icon%20-%20academy%20(4).svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse or contribute to our community
  
  ](https://www.bitovi.com/open-source)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

[ Angular ](https://www.bitovi.com/blog/topic/angular) |  May 15, 2023

# 3 Reasons You Should Test Your Angular Component Templates

 Learn why it is important to unit test the template of your Angular components and how it can help catch errors and ensure the correct functionality.

![Kyle Nazario](https://www.bitovi.com/hubfs/People/kyle-nazario.jpeg)

 Kyle Nazario

Share:

[![Twitter](https://www.bitovi.com/hubfs/limbo-generated/_astro/twitter-white.os3xLc3C_Z2nW4or.svg) ](https://twitter.com/intent/tweet?text=) [![Reddit](https://www.bitovi.com/hubfs/limbo-generated/imgs/icons/reddit.png) ](http://reddit.com/submit?url=)

There is a common misconception among frontend developers that you should not unit test the template of a component. This is incorrect. Not only *can* you test your components' templates, but you *should*.

## Reason #1: It’s officially recommended by the Angular team

The [official Angular documentation](https://angular.io/guide/testing-components-basics) says (emphasis mine):

> A component is more than just its class. A component interacts with the DOM and with other components. The class-only tests can tell you about class behavior. They cannot tell you if the component is going to render properly, respond to user input and gestures, or integrate with its parent and child components…
> 
> A component, unlike all other parts of an Angular application, combines an HTML template and a TypeScript class. **The component truly is the template and the class working together. To adequately test a component, you should test that they work together as intended.**

The docs writers are arguing components, unlike other parts of an Angular application, are code *and* markup, so both should be tested.

## Reason #2: Tests should mimic real users

The whole reason to write automated tests is to give yourself *confidence* when shipping code. If you merge in a new feature and all the automated tests pass, you can be more confident your change has not caused a regression somewhere. You can’t be 100% confident because no test suite is perfect, but good tests lower the odds of regressions.

What kind of test should make you feel more confident? I would argue it is a test that mimics real user behavior as closely as possible.

Now, consider this: **no user ever calls a method on your component’s class**. If your component looks like this…

```
@Component({
  selector: 'app-submit-button',
  template: '<button (click)="onSubmit()">Submit</button>'
})
export class SubmitButtonComponent {
  onSubmit() {
    console.log('submitted')
  }
}
```

…the user will never call `onSubmit()`. They will click the button element, and *that* will activate your component’s class functionality.

If we wanted to test this component and mimic real user behavior, we should not directly call `onSubmit()` in our tests. We should simulate a click event on the `<button>` and then see if our component console logs. That way, we are testing whether the template and class work together as intended, just like the Angular documentation recommends.

Testing like this helps catch when the template and class are out of sync. For example, imagine we were in a rush and accidentally wrote:

```
@Component({
  selector: 'app-submit-button',
  template: '<button>Submit</button>' // Forgot the click listener
})
export class SubmitButtonComponent {
  onSubmit() {
    console.log('submitted')
  }
}
```

This component is broken. Clicking the button in it doesn’t do anything. However, this mistake would not be caught by a unit test that directly called `onSubmit()`. By simulating a click on the `<button>` in our test instead, we mimic real user behavior and catch the error.

## Reason #3: Test your component’s public API (the template)

It is generally a good idea to test modules of code through their public API.

For example, imagine you are writing accounting software and need a function called `isInvoiceInvalid()`. This function accepts one argument, `invoice`, and returns a boolean. The “public API” of this function is to just call it and see what it returns. We should test it through its public API by calling it with lots of different invoices and seeing if it correctly identifies which ones are invalid.

`isInvoiceInvalid()` may be using many private methods to judge whether an invoice is invalid, but you, the person writing the test, do not need to know what those methods are. You are simply testing the function through its public API and seeing if it returns the right result. If another developer comes along and rewrites `isInvoiceInvalid()`, your tests should keep running because they don’t care about the implementation of the function. They just care that it works.

For Angular components, the template is the “public API” our users will use. They will tap, click and type into our templates to cause changes in our application. Testing through the template gives us some distance between our tests and the actual implementation of our component. Hopefully, we can change the implementation of our component class without breaking our tests.

For example, here is a [sample Angular project](https://github.com/kyle-n/template-demo) ([StackBlitz](https://stackblitz.com/edit/angular-rks7bk?file=src/app/app.component.ts)) with a basic signup form. The user can type in a username and password. As they type in the password field, they see up to three colored boxes rating the strength of their chosen password. For a password that meets all the requirements, they will see three green boxes.

![3 Reasons You Should Test Your Angular Component Templates - Demo](https://www.bitovi.com/hs-fs/hubfs/3%20Reasons%20You%20Should%20Test%20Your%20Angular%20Component%20Templates%20-%20Demo.png?width=494&height=568&name=3%20Reasons%20You%20Should%20Test%20Your%20Angular%20Component%20Templates%20-%20Demo.png)

The green boxes are a [presenter component](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) that accepts the user’s password strength as an input and [renders it as up to three colored boxes](https://github.com/kyle-n/template-demo/tree/main/src/app/password-strength-indicator).

```
import { Component, Input } from '@angular/core';
import { PasswordStrength } from '../models/password-strength.type';

@Component({
  selector: 'app-password-strength-indicator',
  templateUrl: './password-strength-indicator.component.html',
  styleUrls: ['./password-strength-indicator.component.css']
})
export class PasswordStrengthIndicatorComponent {
  @Input() passwordStrength: PasswordStrength | null = null;

  get strengthColorCssClass(): string {
    switch (this.passwordStrength) {
      case 0:
        return 'red';
      case 1:
        return 'red'
      case 2:
        return 'yellow';
      case 3:
        return 'green'
      default:
        return '';
      }
  }

  get arrayForNgFor(): number[] {
    let numberOfBoxes = this.passwordStrength ?? 0;
    return new Array(numberOfBoxes).fill(0)
  }
}
```

```
<div class="container">
  <div *ngFor="let i of arrayForNgFor"
       [ngClass]="strengthColorCssClass"
       class="strength-box">
  </div>
</div>
```

```
.container {
  display: flex;
  justify-content: start;
}
.strength-box {
  width: 30px;
  height: 10px;
  border: 1px solid black;
  margin-right: 10px;
}

.red {
  background-color: red;
}

.yellow {
  background-color: yellow;
}

.green {
  background-color: green;
}
```

`PasswordStrengthIndicatorComponent` is almost akin to a [pure function](https://en.wikipedia.org/wiki/Pure_function). It accepts one input, `passwordStrength`, and displays that many boxes to the user in the template.

The “public API” of this component is its template. It’s the number and color of the boxes displayed to the user. We *must* test the template to see if it works.

## How to test Angular components through the template

`PasswordStrengthIndicatorComponent` is a useful example to demonstrate how to unit test a component through its template. We want to see if it will render one box for each point of the user’s password strength. To test that, we should start with the test file created when we ran `ng generate component`:

```
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PasswordStrengthIndicatorComponent } from './password-strength-indicator.component';
import { By } from '@angular/platform-browser';

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

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ PasswordStrengthIndicatorComponent ]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(PasswordStrengthIndicatorComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
});
```

This code creates a virtual DOM and mounts `PasswordStrengthIndicatorComponent` within it.

Some Angular developers are strongly anti-`TestBed`, on the grounds that it is clunky and slow. I sympathize with this view. It is undeniably painful to do dependency injection with `TestBed`. However, it is still the best way to test *all* of your components, template included. It is also [much faster](https://github.com/angular/angular/issues/12409#issuecomment-599223073) in Angular 9+.

Second, let’s write two helper functions we can use across our tests:

```
// ...
  beforeEach(() => {
    fixture = TestBed.createComponent(PasswordStrengthIndicatorComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
  
  function numberOfStrengthBoxes(): number {
    return fixture.debugElement.queryAll(By.css('div.strength-box')).length;
  }

  function strengthBoxIsColored(color: string): boolean {
    const strengthBox = fixture.debugElement.query(By.css('div.strength-box'))
      .nativeElement as HTMLDivElement;
    return strengthBox.classList.contains(color);
  }
// ...
```

These two functions analyze the template of our test environment to check our “public API” is returning the correct results. We should try to avoid tying our tests too closely to the specific HTML layout of our component, so let’s just count the number of boxes and their color.

Last, let’s test the component behaves as expected:

```
// ...
  function strengthBoxIsColored(color: string): boolean {
    const strengthBox = fixture.debugElement.query(By.css('div.strength-box'))
      .nativeElement as HTMLDivElement;
    return strengthBox.classList.contains(color);
  }

  it('should render one strength box for each point of password strength', () => {
    component.passwordStrength = 2;
    fixture.detectChanges()
    expect(numberOfStrengthBoxes()).toBe(2);
  });

  it('should color the strength boxes red if password strength is 1', () => {
    component.passwordStrength = 1;
    fixture.detectChanges()
    expect(strengthBoxIsColored('red')).toBeTrue()
  })

  it('should color the strength boxes yellow if password strength is 2', () => {
    component.passwordStrength = 2;
    fixture.detectChanges()
    expect(strengthBoxIsColored('yellow')).toBeTrue()
  })

  it('should color the strength boxes green if password strength is 3', () => {
    component.passwordStrength = 3;
    fixture.detectChanges()
    expect(strengthBoxIsColored('green')).toBeTrue()
  })
// ...
```

See? Not so bad.

This test could have gone further and even used [snapshot testing](https://circleci.com/blog/snapshot-testing-with-jest/) to verify that the `green` CSS class actually made the box green, but snapshot tests tend to be brittle. Again, we don’t want to tie our tests too tightly to the current implementation of the component. Checking for the right CSS class should be enough.

## Conclusion

Components are the combination of logic and markup. To make sure they work for your users, you should test both of them, and that they work together correctly.

I know this is a controversial opinion, so I would love to hear your opinion in our [Community Discord](https://discord.gg/J7ejFsZnJ4) in the #Angular channel.

[![Join our Discord](https://no-cache.hubspot.com/cta/default/2171535/224f5741-883f-452d-b142-dd575d07685e.png)](https://cta-redirect.hubspot.com/cta/redirect/2171535/224f5741-883f-452d-b142-dd575d07685e)

Don't hesitate to [schedule a free consultation call ](https://www.bitovi.com/services/frontend/angular-consulting)if you need any assistance. Our team is always here to support you.

 Previous Post

![New in Vue.js 3.3: Two-Way Binding With defineModel Macro](https://www.bitovi.com/hs-fs/hubfs/New%20in%20Vue.js%203.3-%20Two-Way%20Binding%20With%20defineModel%20Macro.png?height=117&name=New%20in%20Vue.js%203.3-%20Two-Way%20Binding%20With%20defineModel%20Macro.png) [ New in Vue.js 3.3: Two-Way Binding With defineModel Macro ](https://www.bitovi.com/blog/new-in-vue-3.3-two-way-binding-with-definemodel-macro)

  

 Next Post

![](https://www.bitovi.com/hs-fs/hubfs/Vue.js%20Reactivity%20System-%20Ref%2c%20reactive%2c%20shallowRef%2c%20%26%20shallowReactive.png?height=117&name=Vue.js%20Reactivity%20System-%20Ref%2c%20reactive%2c%20shallowRef%2c%20&%20shallowReactive.png) [ Vue.js Reactivity System: Ref, reactive, shallowRef, & shallowReactive ](https://www.bitovi.com/blog/vue-reactivity-ref-reactive-shallowref-shallowreactive)

```json
{
  "@context" : "http://schema.org",
  "@type" : "Organization",
  "address" : {
    "@type" : "PostalAddress",
    "addressCountry" : "United States",
    "addressLocality" : "Libertyville",
    "addressRegion" : "IL",
    "postalCode" : "60048",
    "streetAddress" : "1134 Pine Tree Lane "
  },
  "alternateName" : "Bitovi",
  "areaServed" : {
    "@type" : "GeoCircle",
    "geoMidpoint" : {
      "@type" : "GeoCoordinates",
      "latitude" : "41.8781",
      "longitude" : "87.6298"
    },
    "geoRadius" : "5000 km"
  },
  "description" : "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
  "email" : "contact@bitovi.com",
  "image" : "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
  "logo" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
  "mainEntityOfPage" : {
    "@id" : "https://www.bitovi.com/blog/3-reasons-you-should-test-your-angular-component-templates",
    "@type" : "WebPage",
    "description" : "Learn why it is important to unit test the template of your Angular components and how it can help catch errors and ensure the correct functionality."
  },
  "naics" : "541511",
  "name" : "Bitovi Web App Consulting",
  "sameAs" : [ "https://www.facebook.com/BitoviLLC/", "https://twitter.com/bitovi", "https://www.linkedin.com/company/bitovi" ],
  "telephone" : "312-620-0386",
  "url" : "http://bitovi.com"
}
```

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Kyle Nazario"
  },
  "dateModified" : "October 3, 2023, 7:38:17 PM",
  "datePublished" : "2023-05-15 15:57:06",
  "description" : "Learn why it is important to unit test the template of your Angular components and how it can help catch errors and ensure the correct functionality.",
  "headline" : "3 Reasons You Should Test Your Angular Component Templates",
  "image" : {
    "@type" : "ImageObject",
    "url" : "https://www.bitovi.com/hubfs/3%20Reasons%20You%20Should%20Test%20Your%20Angular%20Component%20Templates-1.png"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg"
    },
    "name" : "Bitovi"
  }
}
```