Recently, our DevOps team started developing a new front-end application with Angular (4) where the project was automatically generated by Angular CLI. I was not familiar with front-end code at all, and testing it was a total black box for me. A black box I thought could only be tested with GUI tests and visual tests (comparing screenshots, pixel by pixel), which is slow and unreliable.
So, I went down the rabbit hole of Angular and discovered that there is much more to it than I had thought. I discovered the possibilities of layered test automation in this Angular wonderland.
Layered test automation in Angular
A standard Angular application generated by Angular CLI is provided with tools like Jasmine and Protractor, which ensure that a layered test automation set is at your fingertips. The different layers described in the test automation pyramid of Mike Cohn, are also applicable for Angular.
The fundamentals of the pyramid are laid by unit tests that can be set up using the Jasmine testing framework. Integration testing is made possible by an Angular test module in which the interaction of the component with Angular is simulated. And, to top it off, GUI tests can be executed using Protractor—a tool to interact with browsers. It is similar to Selenium WebDriver, but specifically designed for Angular applications.
Before I go on to the different layers of test automation, let me first roughly describe the structure of a generic Angular component, which often consists of the following files:
– A TypeScript (.ts) file that defines the logic
– An HTML file that defines the frontend structure
– An SCSS file that defines the styling
– A spec.ts file where we define the unit and integration tests.
We use the following guidelines to determine what to test. They have arisen through practice within our team.
What to test with unit tests?
We test logic described in the Typescript file with unit tests. We analyze one function at a time and look at the input, output, boundary values, and if and else statements to determine the test scenarios. Start with a happy flow scenario because from there on out we experienced that it is easier to expand with alternative scenarios. Simulate your dependencies using, for example, Jasmine spies/spy objects. We test private variables and/or methods, via public methods where they are used.
What to test with integration tests?
Within our team we call it “template integration testing,” since we test how the HTML template of our component interacts with Angular using the Angular test module.
That is why we focus on HTML, which is arranged or changed by Angular. We do not test whether hardcoded text elements are loaded, only when there is specific logic to test (when there is a page for multiple languages, for example). Neither do we test specific Angular logic (like ngIf and ngFor), only when we write extra logic within those functions (which would beg the question: why is this logic not in your Typescript file?). Often, one template integration test is sufficient because the logic of the page is already covered by the unit tests. We check multiple HTML elements in one test because then the Angular testing module only must spin up once.
The integration tests are set up in the same file in which the unit tests are defined. The difference is that before each test the Angular test module needs to be configured and initiated with TestBed (check out the Angular testing guide for the specifics). Also, the results are tested by checking the HTML elements of the testing module instead of checking outputs of a function.
What is left to test?
When all the unit and template integration tests are created, we often conclude that the biggest part—if not all—of our new or changed functionality is covered. We only use the Protractor tests to cover the crucial user flows from end to end. To us, in this context end to end means from GUI to the integration with the back-end, since we mock the data at the backend.
Despite these guidelines, we maintain a pragmatic approach because there are exceptions to every rule. Lately, we have been adding a function that makes the page scroll back to the top whenever a certain link is clicked. This functionality was very difficult to test with unit and integration tests, so we chose to only test this with a visual test.
It is great to discover these possibilities in Angular testing because it allows us to keep the feedback loops fast and reliable and suited for a DevOps environment. Just like Alice, I will follow the white rabbit to see if there are more ways to optimize test automation in this wonderland!