The world has changed. UI Engineering is 4 realz. Servers have gotten skinny, UI has gotten
To fulfill its potential, UI Developers need to learn/use some important lessons that used to be the exclusive domain of Server-side Dev. Automated testing is one of the biggest. Automated testing, in a nutshell, allows you to prevent regressions while you work, particularly when you’re developing on a large codebase and/or a codebase worked on by many people. Often, this is hard. Angular.JS provides tools that make it easy (-er).
The Goals of Testing
Because you generally can’t hold your whole project in your mind at once (at least not all the time), it’s easy to do something that looks like it works, from where you’re standing. But you’ve broken something that you’ll notice later, in another situation. Automated testing allows you to notice that kind of error right away, before you’ve lost track of where it happened, and before you’ve made a huge investment in something you’re going to have to un-do.
Testing is also a great way to make explicit your assumptions about how you expect your application to behave, document them, and then ensure that the code and the documentation can’t help but stay in sync. It’s one thing to add some comments about how a function should handle null values, or partial input; it’s quite another thing to ensure that an alarm goes off immediately when you make a change that accidentally violates those assumptions.
Last, testing also just teaches us some important lessons about separation of concerns. To borrow from Angular’s guide to unit-testing:
Unit tests try to answer questions such as “Did I think about the logic correctly?” … In order to answer such questions, it is very important that we can isolate the unit of code under test.
It’s hard or impossible to test code that tries to do too many things at once. You don’t want to be trying to test a sort function, but have that sort function suddenly run off and manipulate the DOM. For starters, you really just want to focus your brain on testing how well it sorts, and understanding the goals and assumptions behind that. But you also can’t even test such a function in a context that doesn’t permit DOM interaction. This is a pain. But it also causes us to notice that writing the function in that way was probably not a good idea in the first place. Much better to separate our concerns, make sure that each piece of the puzzle works well as-advertised, and then blend them together as needed. This lesson applies whether you test or not.
One of the
best phattest parts of Angular.js (especially when used with Yeoman & Grunt) is how easy/possible/powerful automated testing gets for UI developers. Angular provides us with awesome tools and abstractions for building our code in bite-sized chunks and separating our concerns. Then, because those chunks are designed to be fully injectable across different scopes/contexts, our logic is therefore built in ways that are:
- discrete, concise, and more understandable
- easier to build
- easier to test, in general
- easier to test under different assumptions and in different use-cases
Plus, testing is baked right into Angular and into our developer-automation tools, Grunt and Yeoman. Karma.js is a test runner created by Vojta Jina of the Angular core team. It allows you to write your tests in any testing syntax (Jasmine, Mocha and QUnit are the best-known, and supported by Karma out of the box. Jasmine is the default option used by the Angular team.) and then run through all your tests and view the results with a simple `grunt test`. Optionally, you can have the tests run automatically every time you save.
Types of Testing
The general approach to testing a given function or module is called unit-testing, because we’re testing a given unit of code. We can do that with almost any kind of code, server-side or client-side. But there’s another kind of testing for us UI engineers, called end-to-end (E2E) testing.
End-to-end testing allows us to test our code not just as stand-alone functions and modules that take some inputs and produce some outputs, but as an actual application that produces results in the browser. In this way, we can test things like DOM interaction and routing constructs that can only execute properly in the environment of the browser. We can make sure that our code works not just in the abstract, but as a living, breathing interface for humans.
It’s tempting to continue and try to explain a full testing workflow for Angular here, but it’s better to show than tell. And the angular-app project has already done a marvelous job of creating a project you can clone that shows how to create and operate a full workflow, including unit and E2E testing, with varying degrees of automation. Do also check out the Angular guide to unit-testing, plus their dev guide on E2E testing. Then go practice!