Automated Testing in Angular: IntroductionAuthor: Gergely Bikki

This is the first post in the Automated Testing in Angular series:

Having enough test cases for an application is crucial. This way we are providing a clear track of the application’s functionality, making sure we are not breaking anything accidentally. We can use this to prove that the application is indeed functioning properly before cutting a new release. In case new developers are joining the project, there’s a good chance that our automated tests are going to contribute to an easier learning curve.

It’s essential to understand what kind of tests we are talking about here. Some are automated, some are manually tested, some are cheap and some are expensive. We have to make decisions what parts of the application to test.

“We should just write automated scripts for all features, right? That’ll make sure everything always works.” Well, not exactly. Having 100% code coverage is generally not recommended as it causes too much coupling. You’d be basically fixing your tests all the time whenever you fix a bug or add a new feature.

“So then what’s the recommendation?”

There is a graphical representation of this, called the testing pyramid. The pyramid shows the amount of tests we should have along with the cost on the other axis. We should aim for testing essential parts of the application, focusing primarily on business logic and container components. End-to-end tests should ideally not have an overcomplicated nature (avoiding conditional testing for example) and should aim for having a “smoke tests” nature.

The pyramid recommends having around 70% unit tests, 20% integration tests and 10% e2e tests of all test cases. As we go up the pyramid, tests are getting more and more difficult to maintain, thus expensive. Manual testing is still a mandatory step, even though we are automating basically everything in the DevOps pipelines. This means that the tester has to sign off a release by conducting particular regression tests.

These practices are generally applicable to all web applications. Even to Angular applications.

Nowadays Angular projects don’t exist without the Angular CLI. Even monorepos built with Nx are using the Angular CLI under the hood. The CLI installs and configures testing functionality out of the box. Taking a look at the dependencies installed, we can mainly point out:

Jasmine (package: jasmine-core) is a behaviour-driven development framework for writing tests. It is dependency-free, easy-to-use and has a very rich set of features for mocking and stubbing. Although there are more popular alternatives in the Javascript world - Jest, for example -, the Angular team recommends (https://angular.io/guide/testing#setup) using Jasmine as test framework.

Karma (package: karma) is a Node.js based test runner developed by the Angular team. It is very handy for executing tests in a real environment with real browsers for desktop, mobile and tablets. It is compatible with DevOps pipelines as it handles headless browser modes.

Protractor (package: protractor) is an end-to-end test framework also developed by the Angular team. Protractor is a great library to work with, however there are more advanced solutions at this point and we are going to adapt Cypress in a future post.

Since everything comes configured and set-up, you just need to run ng test to invoke your unit/integration tests. This is going to launch Karma and if you are trying this out in your browser, you will see a testing interface open up in your default browser. End-to-end tests are a bit trickier as you will need a version of the application served in order to conduct the actions (button click etc.) of the test cases.

This short introduction to the testing practices summarizes the gist of writing maintainable tests. In the next post we are going to dive into the topic of creating isolated unit tests.