A while ago, a friend of mine, who is just beginning to explore the wonderful world of frontend development, asked me how to start testing her application. Over the phone. I told her that, obviously, I can’t do it over the phone as there is so much to learn about this subject. I promised to send her links to guide her along the way.
And so I sat down on my computer and googled the subject. I found lots of links, which I sent her, but was dissatisfied with the depth of what they were discussing. I could not find a comprehensive guide — from the point of view of a frontend newbie — to testing frontend applications. I could not find a guide that discusses both the theory and the practice, and is oriented towards testing frontend application.
So I decided to write one. And here it is.
Testing, as I define it, is code that checks that the code that you write in your app, also called “production code”, works as expected. Some people refer to this as TDD, but TDD (Test-Driven Development or Test-Driven Design) is a specific methodology of Testing, where the tests are written first and drive the design and implementation of the product.
Frankly, I don’t think it matters whether you write the test before or after the production code, as long as you write enough tests to make you feel confident that your production code is ready. But a lot of people (which I respect) do think it’s important, so I’m laying it here on the table.
Unfortunately, the industry has conflated the idea of Testing with TDD, and thus there is no standard term for code that is written by the developer alongside the production code. I choose to call it Developer Testing, or just plain Testing. If you’re interested in a series of discussions around this issue of TDD vs just plain testing, you can watch these, but I would suggest you watch this after you read the whole series of these blog posts.
No — I am not going to discuss the why of testing. If you don’t want to test your code, then don’t. Too bad for you, as you will be doing manual testing of your web app again and again and again. Those pesky bugs, which you remember fixing a while back, will come back again to haunt your nights. Deploying to production will become a task ridden with instability and fear.
But, no, I am not going to discuss why to test.
Another area that is very confusing, especially to beginners starting to study Testing, is the various types of tests. If you’ve researched the subject, you’ve probably heard about (and this is a partial list): unit tests, acceptance tests, integration tests, end to end tests, component tests, and service tests.
To make matters worse, when one person discusses one of these terms, it is probable that their definition of that term is different from another person’s.
And again, I don’t really care about which term is used, since I believe that there is no hard definition of types of tests. But I do believe that all tests lie in a spectrum where on one side we have unit tests, and on the other end-to-end tests (henceforth to be abbreviated as E2E tests).
Let’s start with the simplest type — the unit test. The unit test is, by definition, code that tests a “unit”. And what is a unit? Well, that depends on your programming language. It can be a function, a module, a package, a class. Even an object (for languages like JavaScript and Scala). In JavaScript, it’s usually a class or a module (a “module” in the npm/CommonJS sense of the world, i.e. a file with code).
The important thing is that this unit is tested in isolation. This is perfect for algorithmic, functional stuff, like a function that counts the number of characters in a string, or a class that has a set of validation functions.
These are easy to test in isolation, because they do not depend on other units at all. But what if the unit I want to test does depend on another unit? Well, we can do two things here: either test the two units together, or mock the other unit.
If we test the two units together, is it still a unit test? This is where the spectrum starts — purists will say that it isn’t a unit test anymore. I say that I don’t care. I tend to still call them unit tests, but if somebody else wants to call them integration tests, or two-unit tests, then I’m fine with that.
What does mocking mean? Let’s give an example:
<a href="https://medium.com/media/87dcfa70dfa5dc859bb7813c7c7e1fc3/href">https://medium.com/media/87dcfa70dfa5dc859bb7813c7c7e1fc3/href</a>
This (admittedly contrived) unit is a module with a writeSumToFile function that accepts two numbers and writes their sum to a file.
But notice that it doesn’t do the writing itself. It uses another unit (fileSumWriter) to do the writing.
To test this unit, we can either pass the real fileSumWriter or we can create a mock implementation that doesn’t really do the writing.
When we pass a mock to the function_,_ then the test is definitely a unit test, in the purest sense of the word. But if we test the two units together, a lot of people will argue that it’s not a unit test.
I don’t care what it’s called.
So on the one hand, we have code that tests one unit. And on the other hand, there is the E2E test — the test of the whole application. Everything is tested in the E2E test, and the application is run in the same setup as in production.
Those are the two ends of the spectrum. The points between these two extremes are where most tests lie — they define an ever increasing scope of testing. In those points between the extremes, more and more of the code is tested, and less and less of the code is mocked.
Some people call the tests between these two extremes “Integration Tests”, but for the TDD-ers, integration tests means a whole different thing, which we’ll discuss in a bit. I am going to use the non-purist name —integration tests — for exactly that: tests that test more than a unit, but don’t test all of the units.
So where do you put your tests? Most people argue that there is a testing pyramid — lots of unit tests, much less integration tests, and a really small number of E2E tests. But let’s leave that discussion to the end of this series of posts, because in this series of posts I want to discuss how to do unit, integration, and E2E tests for frontend code.
The first part of the series, after this post, will discuss frontend unit tests. The second part will discuss integration tests, the third part will discuss E2E tests, and the last part will discuss testing strategies — how much of each tests do you need, and other considerations.
For the purpose of this blog, I wrote a small app — Calculator — that I will use to demonstrate the various types of testing. You can see the source code here. Note that it isn’t yet fully tested, but it will be once this series of blog posts will be done.
So tune in next week for part II, where I will be discussing unit testing.