I’m a huge fan of unit testing front end code no matter what platform I’m coding in. The rule about being smart about what you test is even more important with UI code. Uncle Bob said, “having dirty tests is equivalent to, if not worse than, having no tests.”
While Uncle Bob was talking about poor code in the tests, I believe it also applies to testing the right things. It is very easy to test the wrong things or things that don’t really matter. It’s a bad idea to test something that is a certain color or width (of course there could be cases where this needs to be tested). If your code is adding a class selector to an element, then you should definitely test that the class is being added, and that it’s being added at the right time.
Unit testing will improve your UI code architecture and without it, people will tend to lump a lot of code in the View Controller. I will usually catch myself doing that in iOS View Controllers or in my Backbone Views. If there are unit tests in place then just move it to another class and use the tests to make sure everything is working. This is something that has always been done in the back end, so why not in the front end?
There are three ways to unit test, explained beautifully in this video:
- Return Value Verification. You call a method and make sure the correct value was returned.
- State Verification, an action or method is called and you verify that the state has changed as expected.
- Behavior Verification, an action or method is called and you verify that it calls the correct subsequent method.
I find myself using Behavior Verification the most when testing my UI, and with good reason, the UI is supposed to trigger something.
Unit testing your UI can be tricky though. Most tests will be Behavior Verification so you will have to be well acquainted with mocks, stubs, and spies from your chosen testing framework. That being said, like with all unit testing, if there are too many mocks in a test suite then it’s time to do some refactoring.
AngularJS might be my favorite for testing events because when you subscribe to an event it returns a deregistration function for the listener. All you have to do is spy on that returned function when the scope is being destroyed. Memory leaks with zombie event receivers are never good. Tip: when the scope is destroyed, AngularJS automatically takes care of removing objects that are $watch-ed so you don’t have to worry about those.
I hope this post was helpful and gave you a few pointers. How do you test your UI? Let me know if I left anything out!