{ "id": "guide/testing-services", "title": "Testing services", "contents": "\n\n\n
To check that your services are working as you intend, you can write tests specifically for them.
\n For the sample app that the testing guides describe, see the
For the tests features in the testing guides, see
Services are often the easiest files to unit test.\nHere are some synchronous and asynchronous unit tests of the ValueService
\nwritten without assistance from Angular testing utilities.
Services often depend on other services that Angular injects into the constructor.\nIn many cases, it's easy to create and inject these dependencies by hand while\ncalling the service's constructor.
\nThe MasterService
is a simple example:
MasterService
delegates its only method, getValue
, to the injected ValueService
.
Here are several ways to test it.
\nThe first test creates a ValueService
with new
and passes it to the MasterService
constructor.
However, injecting the real service rarely works well as most dependent services are difficult to create and control.
\nInstead you can mock the dependency, use a dummy value, or create a\nspy\non the pertinent service method.
\nPrefer spies as they are usually the easiest way to mock services.
\nThese standard testing techniques are great for unit testing services in isolation.
\nHowever, you almost always inject services into application classes using Angular\ndependency injection and you should have tests that reflect that usage pattern.\nAngular testing utilities make it easy to investigate how injected services behave.
\nYour app relies on Angular dependency injection (DI)\nto create services.\nWhen a service has a dependent service, DI finds or creates that dependent service.\nAnd if that dependent service has its own dependencies, DI finds-or-creates them as well.
\nAs service consumer, you don't worry about any of this.\nYou don't worry about the order of constructor arguments or how they're created.
\nAs a service tester, you must at least think about the first level of service dependencies\nbut you can let Angular DI do the service creation and deal with constructor argument order\nwhen you use the TestBed
testing utility to provide and create services.
The TestBed
is the most important of the Angular testing utilities.\nThe TestBed
creates a dynamically-constructed Angular test module that emulates\nan Angular @NgModule.
The TestBed.configureTestingModule()
method takes a metadata object that can have most of the properties of an @NgModule.
To test a service, you set the providers
metadata property with an\narray of the services that you'll test or mock.
Then inject it inside a test by calling TestBed.inject()
with the service class as the argument.
Note: TestBed.get()
was deprecated as of Angular version 9.\nTo help minimize breaking changes, Angular introduces a new function called TestBed.inject()
, which you should use instead.\nFor information on the removal of TestBed.get()
,\nsee its entry in the Deprecations index.
Or inside the beforeEach()
if you prefer to inject the service as part of your setup.
When testing a service with a dependency, provide the mock in the providers
array.
In the following example, the mock is a spy object.
\nThe test consumes that spy in the same way it did earlier.
\nMost test suites in this guide call beforeEach()
to set the preconditions for each it()
test\nand rely on the TestBed
to create classes and inject services.
There's another school of testing that never calls beforeEach()
and prefers to create classes explicitly rather than use the TestBed
.
Here's how you might rewrite one of the MasterService
tests in that style.
Begin by putting re-usable, preparatory code in a setup function instead of beforeEach()
.
The setup()
function returns an object literal\nwith the variables, such as masterService
, that a test might reference.\nYou don't define semi-global variables (e.g., let masterService: MasterService
)\nin the body of the describe()
.
Then each test invokes setup()
in its first line, before continuing\nwith steps that manipulate the test subject and assert expectations.
Notice how the test uses\ndestructuring assignment\nto extract the setup variables that it needs.
\nMany developers feel this approach is cleaner and more explicit than the\ntraditional beforeEach()
style.
Although this testing guide follows the traditional style and\nthe default CLI schematics\ngenerate test files with beforeEach()
and TestBed
,\nfeel free to adopt this alternative approach in your own projects.
Data services that make HTTP calls to remote servers typically inject and delegate\nto the Angular HttpClient
service for XHR calls.
You can test a data service with an injected HttpClient
spy as you would\ntest any service with a dependency.\n
The HeroService
methods return Observables
. You must\nsubscribe to an observable to (a) cause it to execute and (b)\nassert that the method succeeds or fails.
The subscribe()
method takes a success (next
) and fail (error
) callback.\nMake sure you provide both callbacks so that you capture errors.\nNeglecting to do so produces an asynchronous uncaught observable error that\nthe test runner will likely attribute to a completely different test.
Extended interactions between a data service and the HttpClient
can be complex\nand difficult to mock with spies.
The HttpClientTestingModule
can make these testing scenarios more manageable.
While the code sample accompanying this guide demonstrates HttpClientTestingModule
,\nthis page defers to the Http guide,\nwhich covers testing with the HttpClientTestingModule
in detail.