Unit tests in Angular

Introduction to Angular and the importance of Unit Testing

 

Angular is a popular framework for building interactive and complex web applications. It allows developers to create responsive, dynamic single-page applications using TypeScript, a superset of JavaScript that adds static typing and other features that improve code quality and maintainability. 

Unit testing in Angular is like a safety net for applications, it ensures that individual units of code, such as functions and components, work as expected. It allows developers to catch errors early, makes sure that each component or service behaves as intended and ensure that changes do not break existing features. Although writing tests requires an initial time investment, it ends up making maintenance easier by minimizing manual testing. 

 

Setting up the Testing Environment 

 

To create and run tests in Angular, two tools are needed: Jasmine and Karma.

Jasmine is a testing framework that provides the functions and syntax needed to organize and structure tests. It helps organize tests by grouping related tests into describe blocks and defining individual tests with it blocks. Jasmine’s intuitive syntax makes easy to read and understand test cases. 

Karma is a test runner, developed by Angular team, that automatically executes tests whenever changes are made in the code, so developers don’t have to manually rerun tests after each update. The karma.conf.js configuration file allows developers to set options like which browsers to test in, which files to include or exclude, and many other configurations. This setup helps customize the test environment to fit the project’s needs.  

For installation, in a new Angular project, the Angular CLI automatically sets up Jasmine and Karma as part of the default testing environment. To add or update the testing setup in an existing project, start by running ng add @angular/cli, which installs any necessary testing dependencies if they aren’t already present. This command ensures the project is fully prepared to support unit testing.  

After setting up the basic environment, additional configurations can be made in karma.conf.js as needed. Once these configurations are complete, tests can be executed with the command ng test, which launches Karma to run the tests and displays the results. 

 

Structure of an Angular Unit Test File 

 

Each test file generally includes the following sections:  

TestBed – is Angular’s main tool for setting up the testing environment. It prepares everything needed to test a specific component or service by providing the necessary dependencies. To use TestBed, the TestBed.configureTestingModule method is called, allowing specification of the components, services and modules needed for tests. This step is essential because each component or service rely on certain dependencies to work properly. 

await TestBed.configureTestingModule({
 imports: [MatMenuModule, MatDatepickerModule, Shared TestingModule], 
 declarations: [ActionsBarComponent, HasAuthorityDirective],
 providers: [DatePipe],
}).compileComponents();

 

The imports, declarations and providers sections should include any modules, components or services that the component under test depends on to ensure everything works correctly during testing. 

 

beforeEach – is a hook which is called before each individual test case. It is used to initialize variables, set up the testing environment and reset any states. 

afterEach – is a hook which runs after each test case, often used to clean up data or reset states that might affect other tests. 

Fixture – is a testing utility that creates a testing environment for a component by giving direct access to its logic and DOM. The fixture also includes change detection, which automatically updates the component whenever its data changes, similar to how Angular keeps components updated in a live application. This makes it possible to test how a component reacts to changes in real time. 

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

 

It blockis where each test is defined. Inside each it block, there is usually an expectation that verifies if the functionality works as expected. The description given to it should clearly state what behavior is being tested, making it easy to understand what the test is checking. 

 

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

 

In this example, the it block is checking if the component is created successfully by checking that it is ‚truthy’(it exists and is properly defined). 

Jasmine offers various matchers to check expected outcomes:  

  • toBe: checks for strict equality(by reference). 
  • toEqual: checks for deep equality(by content). 
  • toBeTruthy/toBeFalsy: Verifies if a value is true or false, such as if it exists or is null.. 
  • toContain: Checks if an array or string contains a specific value. 

 

Spies – are tools that track calls to function or methods and they are especially useful for testing components that depend on services or other functions. By using spies, developers can check if a specific function was called, how many times it was called and what arguments were passed to it.  

 

it('should call "read" method with the correct parameters', () => { 
 const questionId = '1';
 const resourceUrl = 'Question';
 service.readAnswers ByQuestionId (questionId).subscribe();
 expect (readSpy).toHave BeenCalledwith (resourceUrl, questionId); 
});

 

 

In this example, the expect(readSpy).toHaveBeenCalledWith(…) check verifies if the readAnswerByQuestionId method was called with the correct parameters. If it was called as expected, the test passes, otherwise, if fails. 

 

 

Conclusion 

 

By following these principles and practices, unit testing in Angular helps ensure that every component, service, and module functions as expected. Writing effective tests allows developers o confidently maintain and expand Angular applications, catching issues early and improving code stability. 

 


 

Author: Diana Ignat, Developer

With her positive energy, kindness, and great ideas, Diana makes every collaboration a joy. She’s always ready to help, listen, and bring people together—usually with delicious homemade treats! We’re lucky to have her bright spirit and generosity on our team.

SEE HOW WE WORK.

FOLLOW US