How to start writing unit tests?

Photo by Andrew Neel on Unsplash

Often you read those articles about unit testing or TDD, you see examples of fully tested code and maybe you wonder — it looks so simple, why haven’t I started doing this yet?

Perhaps, as it was in my case, you come with this knowledge to your project, and… you have no idea where to even start it. It’s easy to try testing on a small, example project that someone showed you to prove that you don’t have to feel scared about it. But it’s different when it comes to practice. But hey, there’s good news! If you already made this first step and feel convinced that you’d like to add some tests to your code, you still can do it. You just need more than a simple article showing you how can you write tests. You need a proper framework. You need a schema that you can follow every time you’d like to test something, so you can take it and start testing in your project.

Let’s make an exercise here. Go to your project (can be any you have in mind, but it’s the best when you’re familiar with the whole structure of the code) and grab some class you’d like to test. Before doing that you may ask yourself a question…

That’s the first decision we need to make. When it comes to testing, you have a few options. It can be a class that has some complicated logic that you’d like to cover with tests to make sure nothing will blow in your face when you don’t expect. It can be some UI class that you’d like to check if it’s gonna look ok in different scenarios. Or it can be class with utilities that you keep to make writing code simpler. Can be anything. If you don’t know what you should use, just take the main screen of your app. You can change this decision later.

Pro tip — it doesn’t really matter at the beginning, so don’t feel super focused on it. As you’ll be more natural with your testing, you’ll feel which classes are better candidates for testing, and which are not, but at the beginning probably most of your classes should be tested (if you don’t have any tests in your project).

In my case, I picked up a class to show the list of articles in a feed.

Now, as we already have chosen a class that we’ve committed to cover with tests, it’s time to ask ourselves:

Now, look into your class. What’s the main purpose? If you can answer it shortly, that’s good, it means your class is probably quite small and responsible for a specific thing. If you can’t say, what’s this class exactly for (e.g. it’s mostly for showing a list of products, but it’s also for making some dates modifications), then it probably means that your class is too big or has too many different responsibilities. Why is it important? Because at this step you need to decide what do you want to test and it’s best if you’ll focus on just one thing, as that’s what unit testing is all about. If your class is too big, just try to extract non-main responsibilities to the other classes. You can unit test them separately.

When you already know, what’s the purpose of your class, you can come up with some testing scenarios. For example, if you have a class to prepare your account history based on a transaction list, you can think about different cases, e.g. how the history should look like when this list is empty? What if there are transactions from different months? What if some transactions have positive, and the others have negative values? What if the dates have different formats?

When you think about test cases you should always have at least:

  • happy path scenario (that’s what you think should be a normal case)
  • edge cases (null, empty, zero, negative values)
  • unhappy path (that’s what you think shouldn’t be valid and your test can confirm that)

Of course, you can have much more than this. There are tons of books about preparing unit test cases, but that’s just for a good start.

For my class, the main responsibility is to properly show a different kind of articles. I have few different types there (e.g. with title, body, image, video), they are all optional and can be in different configurations. I’d like to test that for each scenario the article renders correctly.

Now, as we have it, it’s time to think…

You already know what you’d like to test, you open your class (yeah, do it now!) and you wonder… what’s next?

Here’s what we already have: a subject of the test (your class) and different testing scenarios. Let’s start with the simplest one, a happy path.

There’s a template to describe user stories called Given-When-Then. It means that Given some conditions, When something happens, Then some result occurs. You can follow that template in your test. Start with Then part first. What’s the result you expect? For me, I expect to see an article with a title and video. It should be visible When I enter article details, assuming that Given article has title and video. It may sound really silly, of course, when an article has a video, I want to see a video. But nothing is silly in unit testing. There might be really complicated things happening under the hood, but you have simple action and you expect a simple result. Now, look at your code — what’s your Given-When-Then?

When you picked up your scenario, again start from the end — write assertions first. Why? Because it’s much simpler to make puzzles when you see an image on the box.

Your first assertions (depending on the language) can be as simple as assertThat(yourClass.someMethod()).isEqualTo(result)

Now, look at your code. Remember what’s your test subject and try to eliminate all the other dependencies. If you test countdown calculation, don’t worry about the current date — mock it! If you want to check if the user list is shown properly, don’t use real data from API — mock it! If you test login flow, but you use some external library for it — mock it!

That might not be that easy at the beginning. When you write code without tests in mind, you don’t always focus on separating different logics. It might occur to you that you have everything in one class and when you want to test it, you can’t — because it’s all coupled together. Unfortunately, that’s the moment when you need to take a big shovel and start refactoring. It doesn’t have to be a huge refactor, you can just extract classes one by one, piece by piece. But to be able to write unit tests, you’ll need to.

At this point, you’ll probably realize two things:

  1. It’s good to create smaller classes with dependencies because you can separate some portions of logic and you can test it independently
  2. If you test any kind of frontend app (either web or mobile), it’s good to keep your UI and business logic separately. If you have it in one place, it’s often impossible to write a proper unit test

If you don’t know what you should put as someMethod() , because your case is too complicated to be described as just one method, it’s ok to have multiple assertions. If you want to test that filling the form with a valid data triggers progress indicator, and then enables button and makes automatic navigation to the next screen, then… it’s ok, as long as you’re testing one specific scenario.

Depending on your case, you might want to call some actions before and that’ll be your When. The last step is usually arranging context around the test and preparing the environment, e.g. opening the proper screen, creating model data, which is your Given.

Now… Run your test! And don’t worry if it’s not green immediately. It takes time and practice, but it’s worth it. After you’ll see this green light for the first time… You’ll know why!

That’s it! You wrote your first test in your project. And remember, it doesn’t have to be perfect at the beginning. You can test very easy things, or tests may not have that much sense. But as you’ll write them more, you’ll feel more confident about it, and one day you’ll ask yourself… What was I afraid of? And you won’t even remember.

Thanks!

Flutter GDE / Android & Flutter Developer / blogger / speaker / cat owner / travel enthusiast