Front-End can (and should) be unit-tested. The reason that developers avoid it by saying it cannot be tested is mainly due to the fact that what they think should be unit-tested is really hard to do, but the real problem here is not the unit test tools but rather what “they” think should be tested.
In this one I’ll share with you my thoughts on what type of tests should be avoided in Front-End unit tests and why, plus a suggestion or two on what you can do instead.
I witnessed cases where a developer wanted to test whether an animation happened or not, which translated into waiting the time the animation should take and then checking if the animated DOM element is at a certain position or a certain opacity.
Here we have 2 wrongs in a single test. First one is the test waits. It waits the animation duration before the assertion and in my book (and according to others as well) a test should not break the 250ms time limit, and if I’m waiting 500ms (or sometimes more) I am seriously slowing down the speed my tests suites run.
Second one is that tests check your code logic. Element positioning is not code “logic”. Generally when we want to animate stuff we define a certain CSS animation transition and then change the CSS class of a certain DOM element, letting the browser take over and do its stuff. So what I want to test is whether the CSS class has changed or not. I trust the browser does its stuff well.
Test the logic behind CSS modifications instead of the end result of it.
Code which you are not responsible of and have no means or willing to modify should not be tested as part of your source code tests.
Say you have a library which helps you parse strings. If you have a function which uses it, mock the 3rd party library and return a fake response from it. What the library does internally should not concern you for you are only interested on how your code acts upon different results from it. So… mock the results.
This also allows you to run your tests without depending on whether this 3rd party is there or not. The less your tests relies on the environment they run in the more deterministic they are.
Mock 3rd parties and return fake results which support your test cases when you can
In continue to the idea above, there isn’t much sense in testing a Browser’s API. By that I mean, there is no real value in testing the document or window API much as there isn’t none in testing native browser’s API available when developing browser extensions. When testing you’re interested in the results of these APIs, so you might as well mock them.
Again, we’re assuming that the browser is fully covered by it’s own developers, which usually is the case ;)
Rely as little as you can on the environment you’re running on when writing your tests. Use mocks for environment APIs
That pretty much goes without saying, right?
If your are doing unit-tests you are testing the scope of a “class” or a “component”, and it alone. If you find yourself going outside that scope, in terms of not mocking dependent APIs but actually waiting for them to execute their real code, then you’re doing it wrong.
I’m always alerted when I see a Jasmine test which spies over a function and calls it through e.g. spy(myObj, ‘method’).and.callThrough();. You should ask “why do I need to call that function? can I mock it’s response?”. In many cases the answer is yes, which makes the test a lot simpler and less dependent on the application environment.
Unit-tests are not integration tests. Make sure you stay in the scope of you tested code and mock the outer world as much as you can.
(but remember — too many mocks are a code smell which indicates a bad design)
Async operations generally mean integration test, since you are stepping outside the “scope” you’re testing and wait for a response from another component in order to get back and continue.
A common mistake is to create a “server” mock and have it return a response when the Front-End requests it, so that we would be able to test how our code acts on that response.
Doing that means that (A) you rely on that server mock on your unit-tests and (B) that you are waiting for a response, which can be delayed and slow down your test.
When you find yourself in this spot, ask what exactly are you trying to test — is it the XHR or is it your class’s handling the response? The answer usually is the later and if you want to check how your code acts upon a response, simply mock the response by mocking the XHR. Nobody really cares about the server-communication on unit-test scope.
Server mocks are rarely needed to unit-test Front-End code. Save testing time and complexity by mocking Async operations
Front-End unit tests are not there to check if a pixel shifted 2 units to the left. If you think that unit-tests can save you from corrupting the UI your are sadly wrong. This is not what they are for. There are other tools which help testing the UI, but unit-tests are there to keep our code logic safe and working. If someone changed the rules of a certain CSS class it’s not the job of unit-tests to pick this up. Avoid testing positioning, opacity or any other appearance properties on your units.
Unit tests check code logic, not UI appearance. Use the suitable tools which are made for it instead.
I hope that the practices mentioned above will give you a better idea on how to approach your frontend unit testing. Part of the big challenge of writing tests is to stay focused on what each test needs to check and even more importantly what it should not.