Introducing Angular Component Testing
Tags: cypress, angular, testing, javascript
Published: 2022-08-16 19:36:00

Earlier this year, we announced our <u>biggest release</u> of Cypress ever. This introduced a new unified UI, faster test runs, and support for a new type of testing, Component Tests. This initial release included Component Testing support for both React and Vue with the plan to add additional framework support over the next few months. **Today, we are happy to deliver on that promise by adding first-class support for Component Testing for Angular!**

Read on to learn more about:
- How Cypress Component Testing for Angular improves your testing experience with real-time browser rendering, intelligent feedback, and familiar ergonomics

{% embed https://www.youtube.com/embed/weWTHGWe6uU %}

## Why Component Test with Cypress?
Cypress Component Testing provides a testable component workbench for you to quickly build and test any Angular component regardless of its complexity.

Though you can unit test Angular components today using Karma or Jest, Cypress Component Testing provides several main advantages. Here’s why:

- The Cypress Test Runner is **browser-based**, which allows you to test-drive your component’s **styles** and **API** in an **isolated** way that you do not get with headless unit testing via a [jsdom](https://github.com/jsdom/jsdom).
- **Separating** the components from the rest of your website enforces <u>component-driven development</u>, for developing higher quality, more durable UIs faster and more efficiently.
- **Less code!** That’s right, writing tests for your Angular components requires significantly less code and time than with Karma.

## The Nuts & Bolts
One of the main differences between <u>End-to-End and Component Testing</u> is that unlike E2E testing, which relies on an existing server to serve your website that you visit, Component Testing relies on its own `devServer` to serve your Angular components. Once a server is established, we need a way to `mount` your Angular components to the DOM. Let’s take a look under the hood of these 2 main parts of Angular Component Testing.

### Angular Dev Server
One of the things that makes Cypress Component Testing so powerful for Angular applications is that Angular follows a common convention that we can lean on to accurately configure your devServer. **In fact, for most users it requires zero configuration at all!** This is because we are able to read your `angular.json` file and determine everything we need to know to serve your component accurately and reliably. After updating to the most recent version of cypress via `npm install cypress@latest`, you can follow the Cypress Launchpad instructions that configures your angular application for component testing for you!

![Angular Component Testing Project Setup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5uq6o325lvcre2r4q03o.png)

Sounds too easy to be true? Don’t worry, we prepared a **[quickstart guide](https://docs.cypress.io/guides/component-testing/quickstart-angular)** to help walk you through setting up your project with Angular.

**TL;DR** – the configured `devServer` inside of the `cypress.config.ts`:

```ts
import { defineConfig } from 'cypress'

export default defineConfig({
  ...,
  component: {
    devServer: {
	  framework: 'angular',
	  bundler: 'webpack'
	},
    specPattern: 'src/**/*.cy.ts'
  }
})
```

### Angular Mount
The second component (no pun intended) for getting started with Angular Component Testing in Cypress is a `@cypress/angular` mount function to mount your Angular component to the DOM. Thankfully the same Cypress Launchpad steps also handle the configuration of your <u>mount function</u>.

```ts
import { mount } from 'cypress/angular'

Cypress.Commands.add('mount', mount)
```

_See the finished `cypress/support/component.ts`. This allows you to use `cy.mount()` in any component test without having to `import {mount} from 'cypress/angular'` in every spec file._

An important thing to note about the angular `mount` function is that it wraps around Angular’s own [TestBed](https://angular.io/api/core/testing/TestBed) used for testing. This means you get to use the same ergonomically Angular nomenclature that you already use when testing. Let’s take a look at the `@cypress/angular` mount function’s API.

**The mount function takes 2 properties: **the component** you want to mount and **a configuration** used to configure the TestBed (amongst other things). Let’s first take a look at the first property of component.


**Component** is the class name (ie: `ButtonComponent`) or the template string itself (i.e.: `<app-button></app-button>`).

**MountConfig** actually extends the [TestModuleMetadata](https://angular.io/api/core/testing/TestModuleMetadata) used to configure your current angular tests. That means you declare, provide and import the same way you do in your Angular tests today!

```ts
const config: MountConfig<CardComponent> = {
  declarations: [IconComponent, ButtonComponent, ...],
  providers: [MyService, ...],
  imports: [SharedModule, ...],
  componentProperties: {
	// you can even access your components public properties and methods here
	// ie: @Input(), @Output(), etc
  }
}
```

### Before & After
Ultimately your components will use the same angular `TestBed` you already know and love, and also allow you to run them in a real browser so you can interact with the component the way a real user would. This means that mounting components actually gets much simpler using significantly **less code**!

Let’s look at the simplest component test with Angular using **Karma** vs: **Cypress**.

**Karma**

```ts
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { ButtonComponent } from './button.component';

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

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({declarations: [ButtonComponent]}).compileComponents();
  }));

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

  it('should create', () => {
    expect(component).toBeDefined();
  });
});
```

**Cypress**

```ts
import { ButtonComponent } from './button.component'

describe('ButtonComponent', () => {
  it('can mount', () => {
	cy.mount(ButtonComponent)
  }
}
```


## Wrapping Up
For a complete list of updates in 10.5.0, please review our [changelog](https://docs.cypress.io/guides/references/changelog). If this feature is helpful, or if you have other ideas or feedback, let us know [on Github](https://github.com/cypress-io/cypress).

Want to learn more about Angular Component Testing? Check out our [All About Angular](https://community.cypress.io/events/details/cypress-cypress-hq-presents-all-things-angular-with-cypress/) with Cypress webinar. Our own Ely Lucas and myself will be demoing Component Testing in Angular, showing you how to migrate from Protractor to Cypress, and how to use the Cypress Schematic.

You can also [catch our experts](https://2022.ng-conf.org/session-2/migrating-from-protractor-to-cypress-e2e-testing/) at Ng Conf, a 3-day immersion into everything Angular in Salt Lake City, Utah. We’ll see you there!