A few weeks ago, Pascal Precht wrote a blog article on
Testing Services with HTTP with Angular.
In this article, we want to discuss a more advanced topics on DRY Angular testing techniques
using Custom Matchers and Special Helpers.
These are techniques to make your unit-tests incredibly easy to read and to maintain. And to achieve
our learning goals, there are three (3) important testing topics that we want to cover:
- Testing custom Angular Directives
- Building reusable, DRY TestBed Helper methods
- Using Typescript Jasmine Custom Matchers
These techniques are practically undocumented… yet they dramatically improve the quality of
To whet your appetite, here are some sample DRY tests that we will be showing you how to write:
There a several excellent resources already available that developers can read to
learn about Angular testing:
- Angular 2 – Testing Guide (by Gerard Sans)
- Angular 2 – Unit Testing Recipes (by Gerard Sans)
- Testing with the Angular CLI
(by Todd Motto, Jurgen Van de Moere)
- Testing Angular 2 Components (by Ken Rimple)
The biggest take-aways from these articles is the singular concept that instead of manually
instantiating and testing classes, Angular developers should consider using
TestBed.configureTestingModule() to prepare an entire test Angular DI environment for each
test module (*.spec.ts).
We would not use
TestBed.configureTestingModule()when we are testing a service that
doesn’t have any dependencies. It’d be easier and less verbose to just instantiate using
TestBed is for useful for dependencies and injections.
Consider the traditional approach of manual construction:
Note that it’s fine to do it this way, because ServiceA obviously does not need anything
else to be instantiated; it is a self-contained service without external dependencies. So assuming
that this service won’t get any dependencies in the future, this test is the one we want to write.
The Angular TestBed allows developers to configure ngModules that provide instances/values and use
Dependency Injection. This is the same approach developers use in their regular Angular applications.
With the TestBed and its support for for component-lifecycle features, Angular components can be
easily instantiated and tested.
Before each test, we want configure a new, fresh testing module with only the providers,
components, and directives we need for the current test module.
And notice that we just created a reusable Helper function:
This cool utility function will construct an instance of MyComponent [using the configured TestBed]
using any custom HTML template you specify.
At first, this complexity may seem like overkill. But let’s consider two critical requirements
shown in the sample above:
- MatchMedia instantiation requires an injected BreakPointRegistry instance
- MyComponent instantiation requires an injected MatchMedia instance
Even with these requirements, testing developers should NOT have to worry about all those internals just
to test MyComponent. Using ngModule, DI, and Angular… we now don’t have to worry about those
This is just like those real-world scenarios where our components, directives, and services will
have complex dependencies upon providers and non-trivial construction processes.
And this is where TestBed demonstrates its real value!
We are not using external templates nor any other resources or services that are asynchronous.
So we do not discuss the
async()nor the the
1) Testing Angular Directives
With relative ease, developers can find literature on testing Angular Services and Components.
Yet the How-to’s for testing Directives is oddly not well documented.
Unlike Components with their associated templates, Directives do not have templates. This means
that we cannot simply import a Directive and manually instantiate it.
The solution is rather easy! We only need to:
- configure a TestBed that imports and declares the directive(s),
- prepare a shell test Component, and
- use a custom template which uses the Directive selector(s) as attribute(s).
Since Directives usually affect the host element, our tests will check if those changes to the host
element are present and valid.
Testing the fxLayout Directive
So let’s use the Angular Flex-Layout library as the basis
for the following discussions on Directives, Matchers, and TestBed Helpers.
Real-world solutions often provide great examples for reusable techniques.
We will be using both the fxLayout directive and excerpts from the unit test for that directive
to explore testing ideas, techniques, and solutions that we can also use in our own tests.
First, let’s import the FlexLayout library into our own tests and prepare to test the fxLayout
You can see the actual testing code in layout.spec.ts.
But don’t jump there yet! Wait until you have finished reading this article.
Configuring the TestBed, Component Shell, and Helpers
Very similar to the TestBed sample shown above, we will configure a testing module but we will
not import an external test component. My test component
TestLayoutComponent is itself defined
within our test (*.spec.ts) module.
Using an internal test component enables each test module
and use its own custom test component with custom properties.
Here is the initial configuration:
We now have everything we need to write a Directive test quickly.
Wow! That is pretty easy.
The component has been constructed and prepared with the same
processes and DI that your real world application will use.
2) Using DRY Helper Methods
Let’s first write our test using the traditional long-form… one without custom matchers and the
more advanced helper methods.
Since the fxLayout directive will add custom flexbox CSS to the host element, our test logic
here will confirm that the initial CSS is correctly assigned.
Test Directive Logic: Long-Form
The traditional approach would probably implement something like this:
In the code above, we
- defined a custom template with bindings to the component property
- use deeply nested references to get access to the native element,
- test each style individually.
All this in one (1) single test. And truly it is not easily read.
Now imagine that our test module has more than 20 individual
That would be a lot of duplicate code. And there is certainly nothing DRY (“do not repeat yourself”) about
Test Directive Logic: Short-Form
Here is the DRY version that we want:
Now this test is much more readable, maintainable, and DRY. All the complexities of
- forcing change detection,
- accessing the native element, and
- confirming 1…n DOM CSS styles
is now easily encapsulated in a Helper function and a Custom Matcher (respectively).
The custom Helper function expectNativeEl( ) is similar to the standard expect( ) function.
In fact, it is wrapper function that internalizes the
expect( ) call.
It is important to note that these helper methods always return the value of an
And the resulting code change uses a similar notation to our standard training. So
Testing Complex, Nested DOM
For more complex DOM access, we can use the DebugElement’s query feature to select nested DOM nodes.
Angular’s DebugElement has several query features:
query(predicate: Predicate): DebugElement;
queryAll(predicate: Predicate): DebugElement;
queryAllNodes(predicate: Predicate): DebugNode;
Please note that all debugging apis are currently experimental.
Consider the following helper function expectDomForQuery( ):
In this example, we actually want to test the nested DOM node that hosts the attribute fxFlex.
Using another helper method expectDomForQuery( ) makes that easy.
And the resulting code change is again a similar notation to our standard training:
More Special Helpers
Earlier, we showed a code snapshot that had a special helper
The Flex-Layout library has a responsive engine that supports change detection when a mediaQuery
activates (aka when the viewport size changes). The API uses selectors with dot-notation to
indicate which values should be used for which mediaQuery:
Testing these features presents several additional requirements:
- mock the window API
- simulate a mediaQuery activation
- trigger fixture.detectChange() after a simulated activation
- hide all these details from indvidual tests (DRY)
Thankfully, the Flex-Layout library actually publishes a MockMatchMedia class; which we used in our
Let’s explore three (3) very interesting things are happening in this code:
- override DI providers, and
- dynamic injection using
- use special helper activateMediaQuery( ) function that hides all these details
(1) Overriding DI Providers:
tells the TestBed DI system to provide an instance of the
MockMatchMedia whenever any code
asks for an instance of the
MatchMedia token to be injected via DI.
You can read more about the Angular DI systems here:
Dependency Injection in Angular
(2) Dynamic Injection
Our special helper
activateMediaQuery() needs a dynamic injected instance of the MatchMedia token.
fixture instance, we can dynamically get a MockMatchMedia instance from our fixtures
Notice that all this complexity [and construction details] is encapsulated in our TestBed and special
helper… and the individual tests simply make the easy call:
That is very, very cool!
We have only one more tool [in our testing toolkit] to discuss: Custom Jasmine Matchers.
For those developers not familiar with the concepts of Jasmine matchers, we recommend the online
Remember that matchers are used after the
expect() call and should encapsulate complex logic
and reduce code-clutter in our test code.
This allows our test(s) to remain terse, concise, readable, maintainable, and DRY.
And we should always give our custom matcher functions clear, readable names. E.g. toHaveCssStyles().
Building a TypeScript Matcher
expect(...).toBeTruthy(), we want a custom matcher
goal of creating a custom matcher implemented in TypeScript using well-defined types.
- we need to enhance the
- we need to implement custom matchers.
The global Jasmine
expect() method normally returns
value. To use custom matchers
(with types), we want the expect( ) function to support returning either a standard matcher or
a custom matcher.
Here is a teaser that shows how that is done:
We assigned the global expect function reference to a new export that states that
now return references to a NgMatchers interface.
Here are the full implementation details of our
With the above definitions, we can now use
expect(...).toHaveCssStyles(...) without any
We need one to discuss one more addition to our custom-matcher code. Did you notice the call to
getDOM().hasStyle() ? But where does
getDOM come from?
After some inspection of the @angular/core code, we were able to
get a reference to the special Angular DOM Adapter:
Please note that the getDOM() is a private Angular API and may change in the future.
Using a Custom Matcher
Now we are golden with features. Let’s import and use our custom Jasmine matcher.
Notice that we must register the custom matchers in a
beforeEach() call to configure the
matchers for each subsequent test. And now everything is ready for the individual tests:
When to use Protractor + e2e
It should be noted that the above sample tests confirm whether CSS styles have been applied
correctly to the DOM element. Unit tests perform tests logic and state… but those same
tests cannot easily test how those values affect renderings in the UI.
Jasmine unit tests do not test whether the CSS styles or states render the elements in the
browser as expected. Nor do they test renderings across different browsers. Those types of visual
tests are best performed in e2e testing with Protractor and visual differencing tools.
Perhaps you will say: “Wow, this is cool… but totally overkill!” If you are tempted to say that,
then look at all the DRY tests here:
You will quickly see the value of using helpers and custom matchers.
You now have the techniques and tools to create your own custom matchers and deliver quality, terse
- Custom Matchers: a full set of custom Jasmine Matchers
- Special Helpers: a reusable, importable set of Helpers
- Usage Samples: a full DRY set of usages.
The Flex-Layout Helper functions are actually partial applications (function currying). Here is
How to use them.