Using the power of Angular’s dependency injection system, we can fake specific use cases. This is useful for automated tests, but in this article, we’ll look at a way to use it for manual testing.
In “Testing and Faking Angular Dependencies”, we created an Internet Explorer 11 deprecation banner component and added test suites. We didn’t test it in an actual Internet Explorer 11 browser yet.
To make life easier for ourselves, we’ll create a browser faker component that’s only enabled in development mode thanks to a custom structural directive. For fun, we’ll add text pipes to use common string operations in our component templates.
Simulating A Browser Environment
While we should always test in our actual browser targets — in this case Internet Explorer 11, we might want the convenience of easily simulating other browser environments during development without leaving our browser of choice.
Currently, the deprecation banner component has a direct dependency on the isInternetExplorer11Token. Replacing a dependency with another value dynamically would require us to intercept the injector chain with a conditionally inserted ancestor component or directive.
Dynamically Replacing a Dependency using a Class-based Service
The user agent token factory is only evaluated once per module injector and if it’s not replaced in an element injector provided by an ancestor component or directive, we have to use another technique to fake the dependency. We’ll replace the dependency injection token dependency with a class-based service dependency.
First, we extract the Internet Explorer 11 detection from the dependency injection token to our newly created InternetExplorerService class. The Internet Explorer 11 detection token now delegates to the service when evaluating its value based on the user agent.
At this point, the application should still be working. Unfortunately, we broke the test suite, so we restructure it to use the Internet Explorer service.
As already mentioned, we won’t dynamically replace the user agent token declaratively in a template using an element injector. Instead, we’ll change the state imperatively.
Creating an Observable State
Instead of the user agent token, we’ll make the Internet Explorer service depend on an observable which it’ll get from a separate browser service.
We store the current user agent state in a BehaviorSubject
Initially, the behaviour subject is hydrated with the real user agent string from the user agent token. This value is also stored for later use, since we allow to change the browser state through two commands.
We expose the fakeUserAgent method which sets the user agent state to a fake user agent string. Additionally, we allow a dependee to call the stopFakingUserAgent method which resets the user agent state to the real user agent string.
To keep a tidy ship, we even remember to complete the behaviour subject if the service is ever destroyed.
The Internet Explorer service now exposes an observable property called isInternetExplorer11$ which is evaluated whenever the observable user agent property of the browser service emits a value.
All we need now is to have the deprecation banner component depend on the observable Internet Explorer 11 detection property instead of the regular property which we replaced.
In the deprecation banner component, we replace the Boolean isDismissed property with a BehaviorSubject
Instead of assigning a Boolean value to a property, the onDismiss event handler now emits a Boolean value through the isDismissed behaviour subject.
At this point, the application behaves exactly the way it did before we introduced the Internet Explorer service and the browser service. We have the browser state change commands, but we need some mechanism to trigger them.
For this purpose, we will develop a browser faker component that enables us to fake a browser environment for the rest of the application.
The browser faker component injects the browser service. It has a single form control that is bound to a native
As part of the application we’re testing, I created a range of text pipes for component templates. For example, the replace and trim pipes used by the browser faker component.
Now we have a browser faker component, but we only want it to be enabled during development. Let’s create a structural directive that is conditionally rendered in development mode only.
This structural directive simply renders the component or element it’s attached to if the application is running in development mode, as verified by its test suite.
Now, all that is left is to add the deprecation banner and the browser faker to our application.
Now, we can fake a browser environment to ease development and manual testing. Of course, we still need to test the deprecation banner in a real Internet Explorer 11 browser to make sure. Find help to do this in the Resources section.
To be able to simulate a user environment, we created a browser faker component that’s conditionally rendered in development mode. We encapsulated the browser state in a class-based service and had the application depend on it. This is the same service that’s used by the browser faker.
The browser faker is a simple example of faking a dependency in Angular applications. We discussed other techniques to dynamically configure Angular’s dependency injection mechanism.
The application that we used to demonstrate how to fake dependencies in Angular applications is in a StackBlitz project.
The test suite for the application which tests and also fakes Angular dependencies is in a separate StackBlitz project.
Microsoft’s Modern.IE domain has free resources for generating browser snapshots with Internet Explorer. It also offers free virtual machine images with Internet Explorer running on Windows 7 or 8.1.
Look into techniques for configuring and resolving dependencies in an Angular testing environment in “Testing and Faking Angular Dependencies”.
Learn how to provide tree-shakable dependencies and other complicated configurations of Angular dependency injection in “Tree-shakable Dependencies in Angular Projects”. This is the article that our application is based on.
These wonderful people from the Angular community helped review this article:
- Alex Okrushko
- Brad Taniguchi
- Christian Lüdemann
- Mahmoud Abduljawad
- Max Koretskyi aka Wizard
- Nicholas Jamieson
- 🦊 Reactive Fox 🚀
- Shai Reznik
- Wassim Chegham