How to Write Unit Tests for Angular 15 Application Using Jasmine and Enforce Code Quality in a CI Workflow With Github Actions?

Reading Time: 10 minutes
Unit Tests for Angular 15 Application Using Jasmine and Enforce Code Quality in a CI Workflow With Github Actions

Refer to Part 2 of this blog, to know How to Write Unit Tests for Forms in an Angular 15 Application Using Jasmine?

In this blog, we will cover:

  • What is Unit Testing?
  • About Angular
  • About Jasmine
  • Hands-on
  • Conclusion

What Is Unit Testing?

As you develop applications, you write code and as the application features expand, the more moving parts your code has. Over time one has a large code base to maintain and it becomes hectic to ensure quality and at the same time introduce new features. Unit Testing comes to the rescue to ensure that any new piece of code (function) works as intended without having to run into surprises after it is deployed.

Code in Unit Testing is generally regarded as independent functional pieces of code that can work separately to produce the desired outcome by accepting arguments, transforming the data, and returning the desired output. Unit Testing covers mocking these processes by using dummy data to mimic the real thing.

Why Is Unit Testing Important?

Unit Testing is important to ensure that code is deployed free of bugs hence ensuring only quality work is either merged to repositories or shipped to production. Most companies include Unit Testing as part and parcel of their CI/CD workflows to enforce code quality. This will ensure that only proven working code makes it to the repository.

A good practice is sticking to the principles of TDD (Test Driven Development) which insists on the importance of Unit Testing and using successful Unit Tests as a rule of thumb that a feature is of good quality and ready to be deployed.

About Angular

Angular is a JavaScript framework that uses TypeScript as its core development language and it comes ready with all the necessary tools to build a fully-fledged Single-Page Application. These tools include an inbuilt router, HTTP library, and rxjs for handling Observables. Angular uses an Imperative programming approach whereby it focuses on “what” and “how”. This is because Angular has a defined way of architecting components, using directives, services for API calls, modules for bootstrapping isolated pieces of code, and concepts such as dependency injection. Angular comes with Angular CLI which is a CLI tool that enables you to manage your projects. To install Angular CLI, we install it globally via npm using the command npm i -g @angular/cli. As of the time of writing this blog, Angular is at version 15.

To create a new project, we use the command ng new <project_name>. You will be prompted to select a couple of options such as routing; go with yes for routing and SCSS for the stylesheet.

Unit Tests for Angular 15 Application

To serve our project, we use the ng-serve command.

About Jasmine

Jasmine is a framework for testing JavaScript Code; specifically Unit Tests. Angular comes with Jasmine as the default tool for Unit Testing. To run Unit tests, we use the ng test command; It’s preferably better using with the code coverage flag to get a coverage report i.e ng test –code-coverage.

Unit Tests for Angular 15 Application

This will automatically open a browser window that displays the test suites as shown below:

Unit Tests for Angular 15 Application

Note: During CI setup, we shall fine-tune Karma(the test runner for Jasmine) to run headless and also to enforce coverage thresholds since we won’t have a GUI.

To get the coverage report displayed on a web browser, go to your project directory there is a coverage folder; expand it and run the nested HTML file with Live Server extension on VS Code(not sure of other IDEs/Editors). It should appear as below.

Hands-on

How to Write Unit Tests for Angular 15 Application Using Jasmine and Enforce Code Quality in a CI Workflow With Github Actions?

We will be developing a shopping cart application using Angular 15, Angular Material, and TDD approach.  

We shall have a simple checkout form to enable users to place their orders. To add to that, we will have a Github Actions pipeline that checks code quality before accepting a PR(Pull Request). The repository for the code used in this blog post is at https://github.com/workfall/workfall-jamine-ng/tree/v0.1.0.

Project Structure

Our project is simply structured as follows:

Unit Tests for Angular 15 Application

Our views will be modules so that we can lazy load them.

We’ll create our first component using the ng generate component components/nav-bar –module=<module_name> command or you can aliases i.e ng g c components/nav-bar –module=<module_name> the –module=<module_name> flag specifies the module in which to register the component, if not specified, they will be automatically registered in AppModule. We’ll create other components using the same command. For services, we shall use ng g s <service_name>, and finally for modules we shall use ng g m <module_name>.

Modules

Our modules folder will contain SharedModule which will register all the components or modules that will be shared across the application and also in other modules. It is very important to note that. To generate the module use the ng g m modules/shared command.

Components

Our components folder will contain all the shared components for the entire application. This component will be registered in the SharedModule. This SharedModule will be imported into AppModule.

UI Library

For the UI library, we are going to use Angular Material simply because it’s part of the Angular ecosystem. Therefore, things don’t feel so much out of place and gels well with Angular itself. To add Angular Material use the ng add @angular/material command.

Unit Tests for Angular 15 Application

Creating Our Code Pipeline on Github Actions

Since we shall be enforcing code quality, we need to create our pipeline first so that we have standards set at the very start. As of now, you should be able to see that the Code coverage tab on your browser automatically updates when you add new components and services.

Note: The *.spec.ts files should mimic the corresponding file’s module by having modules imported into the configureTestingModule method. This is very important to note.

Unit Tests for Angular 15 Application

Github Branch Settings

Let us switch over to our repository with our initial commits.

In the repository, we shall go to settings > Branches. Makes sure that it is checked as shown below:

Unit Tests for Angular 15 Application

Then click on Create.

Workflows

On a dev branch, create a directory .github > workflows > main.yml

The main.yml file is as shown below:

Unit Tests for Angular 15 Application

Create a karma.conf.js file in the root folder of the project which should appear as shown below: (we shall set our coverage threshold to 95% and throw an exception if they are not met)

Make some changes to the angular.json test section to accommodate the Karma configurations.

angular.json:

We shall also add a new npm script test-ci to be used in the pipeline.

package.json:

Writing Unit Tests Using the TDD Approach

With that completed, time to write some features with the TDD approach. The first feature will be product display. So, we’ll begin by writing tests that are failing. For the backend service, we shall be using https://fakestoreapi.com/ to act like our backend.

Let us start by writing unit tests for our services. To create a service, we shall use the ng g s <service_name> command. In this case ng g s api. To test services, we write test cases in the .spec file majorly to validate the responses and HTTP calls being made to the backend.

api.service.mock-data.ts:

api.service.spec.ts:

The test case above expects an array of products from getProducts method in the ApiService. This data will come in streams hence an Observable that is why we use the of method from rxjs.

api.service.ts:

If we look at our test coverage results after using the ng test –code-coverage command, you’ll realize that we are still at 100% including our services which is a plus.

Unit Tests for Angular 15 Application

Next, we shall create a simple UI for displaying the products. Let’s start by creating a view for the products using ng g m views/products –routing. This view will be a lazy-loaded module with its internal routing. We also have to register the root component for this module with ng g c views/products –module=products. Next, we shall modify the routing to have the products module loaded on the home route.

products-routing.module.ts:

Unit Tests for Angular 15 Application

products.module.ts:

Note: Remember to import SharedModule into this module

Unit Tests for Angular 15 Application

app.component.html:

app-routing.module.ts

When you go to http://localhost:4200 you should see this:

This shows that our product’s view is the entry point of our application. Next, we shall create another module to handle all imports of angular material; ng g m modules/material

material.module.ts

Note: import MaterialModule into SharedModule.

nav-bar.component.html:

nav-bar.component.scss:

products.component.html:

The web page should appear as shown below:

Next, we shall use MatGridListModule for defining our product layout. Therefore, import into material.module.ts.

products.component.html:

If you get the error: If ‘mat-grid-list’ is an Angular component, then verify that it is part of this module, import MaterialModule into products.module.ts.

The result should be as shown below:

Now, let us work on the UI of our product component.

product.component.html:

Note: We are hard-coding this now because we are interested in testing out the design. We will make this template dynamic.

product.component.scss:

Result:

Next, let us write test cases for this feature starting with the product component. You will realize that tests are failing because Testing Modules are not configured correctly (always ensure that the Tesing Module is configured the same as the real module; add the missing modules to the Testing Module i.e SharedModule and MaterialModule).

product.component.spec.ts:

We have to write the component to pass the tests. Now, this is the first step in TDD.

product.component.ts:

Note: If you encounter an error like: Class is using Angular features but is not decorated. Please add an explicit Angular decorator; downgrade TypeScript using the npm i typescript@4.8.2 -D –save-exact command.

Next, we shall write tests for the product’s feature module.

product.component.spec.ts:

Since we shall be injecting the ApiService, we need to provide it for the Testing Module. It is a bad practice to Inject the Service directly, it is important to completely avoid making any HTTP calls in Unit Tests. Hence we shall create a service stub, which is simply a class with the same methods and return types as the actual service.

api.service.stub.ts:

products.component.spec.ts:

products.component.ts:

products.component.html:

Final Result:

Final tip: Sometimes the trick is in adjusting your mock data to cover edge cases such as conditionals. Refer to the tests for the rating feature in the product. Another good way to do this is to separate the rating feature into a component because it may be reused in several places in the application.

Conclusion

In this blog we demonstrated how to write Unit Tests for Angular 15 Application Using Jasmine and Enforce Code Quality in a CI Workflow With Github Actions by following these steps:

  • How to write Unit Tests for Angular components and Services using Jasmine.
  • How to enforce Code quality by checking Unit Test coverages threshold with Karma and Github Actions CI.
  • How to run Karma in a non-GUI environment i.e CI pipeline.

Stay tuned for the part 2 blog on how to write Unit Tests for forms in Angular. We will come up with more such use cases in our upcoming blogs.

Meanwhile…

If you are an aspiring Angular Lover and want to explore more about the above topics, here are a few of our blogs for your reference:

Stay tuned to get all the updates about our upcoming blogs on the cloud and the latest technologies.

Keep Exploring -> Keep Learning -> Keep Mastering 

At Workfall, we strive to provide the best tech and pay opportunities to kickass coders around the world. If you’re looking to work with global clients, build cutting-edge products and make big bucks doing so, give it a shot at workfall.com/partner today!

Back To Top