Security Guard at Rockefeller Center, NYC. © 2018 Jared Youtsey

CanDeactivate guards are often used to prevent a user from navigating away from a dirty form, one with unsaved edits. I found myself in this situation at work today and could not find a solution on the web of how to unit test the guard. The problem lies in that the guard is a service that is dependent on a component, not the other way around. It’s exactly backwards of every pattern I’d seen to date. So, I dug in and started experimenting and gained a greater understanding of the Angular Dependency Injection inner workings. In the end, if you use an interface instead of a hard component dependency it all makes much more sense.

I’d recommend forking the repo and playing with it to understand how it works first. We won’t dwell on the components and the UI, just on the guard and the implementation of the tests. You’ll want to look at the GuardedComponent and understand its canDeactivate method, but for testing we just need to understand it as it gets completely mocked away.

First, we start with an interface that will allow us to deal with any component that needs this guard in an abstract way. This will make mocking much easier.

The guard itself is very straightforward. It implements CanDeactivate<T> where T is our interface. It verifies that the component that implements that interface does so correctly (line 11) (Note: this seems to be a common pattern just in case you managed to fool the compiler?) and then it calls the interface method. That’s it.

In the unit test we want to mock away the actual component that implements the interface. We don’t want to have to import and provide all of the Component’s dependencies in our test. In our spec file we’ll add a MockComponent that implements the interface. (Note: we haven’t decorated this with @Component but we could and it would still work.) On line 3 we’ve added a returnValue: boolean that we can set in our tests so that we can mock the real component’s canDeactivate responses.

Now, let’s look at our test setup:

Notice line 9. We are providing our mock just like a service! Yes, this really works! Even without all the usual decorators.

Now, all we have to do is set the mockComponent’s returnValue for different scenarios.

In the first test we return a boolean from our canActivate in our component. The real component does this when, for example, the form isn’t dirty. If you try to navigate from a pristine form, the guard won’t stop you. In this case, we’re just deciding based on whether or not the user toggled the guard on in the UI.

In the second and third tests we return an Observable, like the user turned the guard on in the UI. That would cause the PrimeNG confirmation dialog to be shown. It has accept() and reject() methods that will call next(true) and next(false) respectively on the Subject that backs the Observable.

Admittedly I had built all of this without the interface at first. So, my mock extended GuardedComponent. It WAS a component. So, I had to import all of my dependencies. And seeing a component as a provider was a very backwards thing! But it did work! The interface cleans things up quite a bit and is more in line with the official Angular docs.

I’m new to this “blog what you learned” thing. I hope you found this helpful and I am curious to see if you have tackled this subject in a different manner.

Thanks for reading, and leave me some claps if you enjoyed this article.