I Hate Testing Angular Applications
First, a confession: I recently wrote a blog post about unit testing an Angular application. Well, as it turns out, what I was in fact doing was trying to convince everybody of the joys of Angular testing. Including myself.
I swear. I truly love testing and test-driven development (TDD). I’ve done it for Python and Ruby projects. Everything they say about TDD is true: it helps write better code, it’s a safety net, it’s great project documentation in itself, and, personally, it really helps me focus. There are times when I know I have to do something, but it’s so complex that I feel overwhelmed. Tests come to the rescue!
When I just focus on writing one simple, lovable test that states a little bit of what I know I have to do, it gets the ball rolling, and, suddenly, after having felt lost, the only thing I now have to do is turn reds into greens. I’m not trying to convince anyone of the greatness of TDDs; I really feel this love. But… When I started writing an Angular application, all that love turned into hate.
Honeymoon’s Over
I still don’t completely understand where all that hate came from. Where did Angular testing and I go wrong? I really did try to understand it better. Maybe someday we can come to terms with our situation. We will probably never recapture our fairy-tale love story, but maybe, at least, we can have a relationship where there’s mutual admiration and respect. Maybe we can actually be friends.
But not right now.
First critical topic: dependencies
Dependency injection is a very cool concept and a powerful tool. But as with everything in life, with great power comes great responsibility. Of course, if you follow good coding principles, you will have small components with limited scope and responsibilities that also depend on only a few things. So you will only inject a few dependencies — seven max they say —, and when you write your tests, you will happily isolate your tested code from all the dependencies by injecting mocks. This tale is so beautiful, I can almost hear the birds singing.
But then reality comes rushing in. An evil queen that whacks me in the back of the head: I have a fairly complex Angular application in which, as much as I try, I can’t keep dependencies below seven for several services, and, honestly, for some controllers either. And when I want to write the tests, I have to mock and inject all those dependencies, every time. Ng-mocks, Jasmine spies… all those tools are great, but just having to write them for every public method in every service I am injecting makes that first beforeEach block 80% of the test file. And please don’t ever think about changing a service after having written tests because you will have to go back and change all the mocks you wrote for all the tests that used that dependency. And don’t get me started on mocking a promise!
Another thing that adds to my hate is how hard it is to make integration tests. I feel that most integration tests in a web app touch either the server, the user interface or both. E2E tests will make real server requests and depend on the server availability. That’s actually not that bad and is the idea of E2E tests. But if you want integration tests that don’t really depend on the server, you will have to mock its responses. That can be complicated depending on the amount and structure of the data you are handling. As for the user interface… let’s just say that I really appreciate the wonders of the human brain and its powerful imagination when I try to code what a person would do when interacting with a web page.
So let’s give up and stop doing TDD. People have lived without it for centuries. Period. End of story.
Moving On
Yeah right. As if that’s what I’m going to tell my client! And the truth is that, as with finding true love, I believe you should never give up. So let’s keep trying.
I decided that either I was doing everything wrong, and it would be worth spending some time learning more about good developer practices, or I was right, testing was hard, and it was worth spending some time looking for tools to make my life easier.
That’s when I read this: “Angular testing isn’t the most exciting or attractive part about coding. It’s important, of course, but it can be kind of hard to get started. You know you should, you kinda feel guilty that you don’t, but there’s something holding you back.” Those were the most beautiful words I had read in a long time! It wasn’t just me!! What a relief. Not only because it preserves my self-esteem, but also because if it’s not only me, for sure there must be other people who’ve come up with ideas and there must be tools to help with this. And, indeed, as predicted, there are other people who’ve come up with ideas and there are tools that help make Angular testing easier (I’m not going to pretend that everything will be pure joy from now on).
bardjs
The first tool that I find invaluable is bardjs. This library really helps reduce the boilerplate test code.
Dependency injections
Using the library greatly simplifies the injections, reducing to one line what could take many, many lines.
This:
var oneService, $q, $rootScope, anotherService;
inject(function(_oneService_, _$q_, _$rootScope_, _anotherService_) {
oneService = _oneService_;
$q = _$q_;
$rootScope = _$rootScope_;
anotherService = _anotherService_;
})
Turns into this:
bard.inject('$q', '$rootScope', ‘oneService’, 'anotherService', ‘aConstant’);
Dependencies mocks
It also helps reduce the boilerplate, and makes my life a lot easier, with mocking dependencies.
Usually, we only need one element of a service for our test, but still have to mock all or the test would break.
bardjs helps turn this:
var oneMockService = {
functionCool: jasmine.createSpy(functionCool),
oneMoreFunction: jasmine.createSpy(oneMoreFunction),
reallyMoreMocks: jasmine.createSpy(reallyMoreMocks),
andWeHaventFinished: jasmine.createSpy(andWeHaventFinished),
itCouldGoOnAndOn: jasmine.createSpy(itCouldGoOnAndOn)
theOnlyOneWeNeed: jasmine.createSpy(theOnlyOneWeNeed).and.returnValue(‘myTestValue’),
};
module(function($provide) {
$provide.value("oneService", oneMockService);
});
Into this:
bard.mockService(oneService, {
theOnlyOneWeNeed: jasmine.createSpy(theOnlyOneWeNeed).and.returnValue(‘myTestValue’)
});
Automate tests running
After finding this lovely library, I was encouraged to work a little harder to overcome other obstacles I’ve found along the way. For example, having to force Karma to reload the files when running the tests from WebStorm.
I found this issue, which offers some solutions to what appears to be a WebStorm bug. Implementing these suggestions did solve the problem, allowing me to do something even more helpful: configure the automatic run of the test every time I made code changes. Now, I was able to disable single run in Karma (singleRun: false) and toggle auto-test on WebStorm.
How Is the Love Story Now?
Just these two improvements have made a difference and are really helping me think about tests when writing new code. I’m not in love yet, but I see a small light at the end of the tunnel.
Maybe I’m building a beautiful relationship that wasn’t love at first sight, but has enough of a foundation to grow strong.
Or maybe I am just a hopeless romantic.
Either way, I refuse to give up. Angular testing and I will figure this out!
Want to be alerted when we publish future blogs? Sign up for our newsletter!