All You Need to Know About Mocking in Angular Tests (2020)

Share on facebook
Share on google
Share on twitter
Share on linkedin

You probably know it is a good idea to test your Angular Apps because it pays for itself in the form of easier maintenance and fewer errors. As I explained in my guide to Angular testing, unit tests should be the bedrock of your test coverage as they are the cheapest to maintain. Compared to more sophisticated testing frameworks like XUnit the tools for mocking in an Angular CLI project are not impressive out of the box. Among my favorite mocking frameworks are NSubstitute for C# applications, as it requires very little code to mock out classes, compared to what it takes to create mocks for Angular services and components. The mocking process can be so tedious that some teams get tempted to either “white-box” test classes by making certain methods public for overriding them instead of creating mocks or don’t create tests at all.

Either way, writing tests should be easy and this post is covering how to make testing easy by covering the best ways to write mocks in an Angular app.

Update:

Note, the methods for mocking explained in this post is fine if you prefer to go with “vanilla” jasmine + Angular Testbed. Nevertheless, I have started to prefer using Spectator (for less boilerplate with test setup + mocking helper for services) and ng-mocks for mocking components, directives, and pipes. I recommend you check these libraries out! It might still make sense to use some of the mocking tricks from this post in conjunction with these libraries.

Billede

Should I create a unit test or an integration test?

You can avoid mocking altogether if you decide to create an integration test but how do you know if you should create a unit test or an integration test?

Create a unit test when:

  • The unit under test contains logic that needs to be tested separately to create a useful test, that is, to ensure that a feature is working as expected
  • The unit contains complex logic that needs to be tested separately

Create an integration test when:

  • When the integration between components is needed for creating a meaningful test
  • When testing a component that only contains simple logic that needs interaction with other components to create a useful test
  • When testing integrations with third-party libraries

When not to create unit nor integration tests

If the feature under tests relies heavily on interaction with the dom and contains no business logic, are often too fragile and doesn’t give enough value to test to justify investing in covering it with tests. You should do a risk analysis to discover the most critical features in the app to invest automatic end-to-end testing in and, if the feature is of critical value, create end-to-end tests to cover the feature.

Dealing with mocks in an Angular app

Now we are gonna look at how to deal with mocking of dependencies in Angular unit tests. The examples are based on my Angular TODO app with best practices repository.

Mocking components

For mocking component, the easiest solution I have found is to avoid needing to mock components by shallow testing, that is, use schemas: [NO_ERRORS_SCHEMA] so the component under test’s template is not instantiating component tags. Of course, if you have a test that needs to interact with another component, such as through a view child, you need to have a mock of the component, if you don’t want to create an integration test.

What you will often see in Angular libraries, eg. Angular Routing library, is that it provides a testing module for easier mocking in unit tests. I recommend that you do the same with components you want to create mocks for, by creating a *component-name*.component.mock.ts beside the component file, so you can easily get a mock of the component. Also, you should make the mock implement the implementation component to ensure that the mock and the component are exposing the same methods.

After having created a mock file for a component, it is easy to get a mock of the component when needed.

An example of a component mock is:

Which is located beside the component file, which gives us a folder structure like this:

The mocks are then imported like this:

I wish there were an easier and less boilerplate way to create mocks for components in Angular, such as creating it with Jasmine spies, but you need to apply the component decorator to the class to make it a component.

Mocking pipes and directives

Mocking pipes and directives is the same principle as with mocking components except when mocking these you can’t do NO_ERRORS_SCHEMA as the compiler will try to invoke a pipe or directive when evaluating the template, so you need to create a mock for every pipe and directive in your template of the component under test if you don’t want to override the component template in the test or do an integration test. For creating the mock, the same principles applies: create a mock file beside the implementation file with: *pipe/directive-name*.pipe/directive.mock.ts and implement the implementation to ensure that mock and implementation enforces the same contract.

Mocking services

Because services are basically typescript classes these are way easier to mock out. This is one of the reasons that I preach to create component services to encapsulate a component business logic in my refactoring Angular apps post.

The Angular testing library provides us with a method called createMagicalMock converting all of a service’s method into jasmine spies, making us able to substitute the values and assert that methods have been called.

The service mock methods are in my SpyHelper class:

The createMagicalMock method will substitute every method to a spy, making it possible to set a return value and assert that calls have been made to the methods.

Compared to creating mocks for components, this is way easier and all you need to do is to provide a service with the provideMagicalMock method and then instantiate the mocked services with TestBed.get:

Notice how I’m overriding the todoListServiceMock’s todoList property with:

(todoListServiceMock as any).todoList = todolist;

This is the name of the game, you gotta be a little bad sometimes. I do this so I can set the value of the service ‘s todoList property. Alternatively, I could have made it a public getter, so I could have used spyOnProperty which could create a spy for the return value.

See how easy this was? Thin down those components by moving the logic to services, so you can write tests with ease.

Conclusion

In this post, we looked at how to mock dependencies in Angular tests. Compared to other test libraries such as XUnit the Angular testing setup is very basic but certain tricks will ease the work of creating mocks in unit tests. We looked at when to use mocks vs. integration tests vs. no tests at all. In the cases where mocks were necessary, we looked at how to create mocks for components, directives, pipes, and services. We saw that services were easiest to mock using the provideMagicalMock helper method to create a service with all methods substituted with spies.

If you liked this post make sure to comment, follow me on Twitter and subscribe for weekly posts about how to become a better Angular developer.

 

Do you want to become an Angular architect? Check out Angular Architect Accelerator.

Related Posts and Comments

High ROI Testing with Cypress Component Testing

Testing is one of the most struggled topics in Angular development and many developers are either giving up testing altogether or applying inefficient testing practices consuming all their precious time while giving few results in return. This blog post will change all this as we will cover how I overcame these struggles the hard way

Read More »

Announcement: Angular Testing Workshop

I’m announcing a new live workshop: Angular Testing Workshop. It will be half-day workshops over three days, 100% online, where we will learn all the industry best practices with Angular testing so you can apply them in your daily work – taking straight out of my experience with doing Angular testing for big projects. The

Read More »

The Most Common Cypress Mistakes

Cypress has become the preferred way of doing UI testing of Angular apps by many Angular experts. It offers great improvements over Selenium-based testing tools by making the testing experience more like a real user using the built-in retry mechanism of assertions and commands (eg. click on the element), a user-friendly GUI which makes it

Read More »

The Ten Commandments of Angular Development

As a consultant, I normally work with companies between 3-12 months at a time and then I am off to the next gig. Most often, I am hired as a “hands-on” coach, were I am called in for an important and urgent project to make stuff happen within a very tight deadline. This requires that

Read More »

The Complete Guide to NgRx Testing (2020)

The main secret behind having better test coverage in an Angular app is to reduce the friction it takes to write tests and enforce test coverage by setting a test coverage threshold to be checked on every commit. NgRx and reactive testing is an area where many people get confused because it seems hard to write

Read More »