Sunday, February 28, 2016

Why bother with Unit Testing, JUnit?

One of "my" students came asking me a very important question that I think it is very hard to explain at a classroom level.

His question was "what is this JUnit thing, and why should I even bother with it?"

See, this makes perfect sense for this student to question this concept to himself. In a classroom environment, specially on low level classes, all of your information, classes and data models are created in a very small scale, and they are all created by you. So if the app compiles and runs, you don't have an error. Because of this Unit Testing, or JUnit in Java, makes almost no sense.

Let's talk about this concept to hopefully clear out for you what Unit Testing is, and why you actually want it.

What is JUnit?

JUnit is a Unit Testing framework for Java. For the rest of this blog post I will stop calling this concept JUnit, and we'll talk about unit testing in general so it can be applied to other languages.

What is Unit Testing?

"Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation. Unit testing is often automated but it can also be done manually."

What does this mean, and why should I care about it?

This brings up back to the original question.
See, Unit Testing is often (read: 99.9% of the time) done automatically, so you should think of Unit Testing as a code you create to validate how you expect your code to work.
This makes more sense in a real-world type of example.

Let's say we are developing an app that downloads the data of all of the students at your school from some school server. This server gives us the first name, last name and age of each student.
For Java, we would create a student object like this:
public class Student {

    private String firstName;
    private String lastName;
    private int age;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

And the server would return data like this:
{
    "students": [{
 "firstName": "Eduardo",
 "lastName": "Flores",
 "age": 20
 }, {
 "firstName": "Mary",
 "lastName": "Johnson",
 "age": 21
 }, {
 "firstName": "Mike",
 "lastName": "Pascal",
 "age": 22
 }, {
 "firstName": "John",
 "lastName": "Smith",
 "age": 25
 }
        // continue for 50000 students
        ]
}
This is a JSON string, but don't worry about that means right now. What matters is that you can see that every student has a firstName and lastName as String, and an age as a int.
We would assume your school also has something like 50,000 students...not just 4. You get the idea.

You create your app, it downloads and parses the data, everything works and you release your app. Life is good!

But then, one day, you hear your app crashed that morning. Then it crashed again when you tried it!

You debug your app, and you find out that the crash is cause by the app parsing the data that comes from the server. In other words, something the server is sending you is making the app crash.

Fixing this scenario without Unit Testing

Let's say you didn't do Unit Testing, and now you need to make sure the data coming from the server is valid. Well, unfortunately for you, the data is valid in general terms (there are no invalid characters) so since you don't have unit testing setup in your app, nothing created by a third party website or service can help you find the issue.

All you get is a crash report from the stack trace saying something about "not of type Integer (int)"

So...you'll have to loop through every single one of the students...all 50,000 of them...one...by...one...until you find the problem.

What's worse is that you created this app 6 months ago, and by now you have completely forgotten how the app works.

Fixing this scenario with Unit Testing

If you would've done unit testing on your application (and done it thoroughly), you can now run the unit test, and within 2 minutes you will know that a particular student has an age of 20.9 (no idea why, but I've seen it happen)

How would you know this?
Your unit test would fail when validating the age field of every student object downloaded, but it will tell you (again, if setup properly) that a student object coming from the server has an age of a double instead of an int.
This one student object is what is causing the crash in your app.

So now instead of having to go one-by-one on the data coming from the server, you can see that student "Jimmy Smith" of age 20.9 is causing the problem. And you did all of this within 2 minutes.

When you should write your Unit Test

Since your unit test is an individual test over a specific piece of data, you should write your unit tests when your app is running and it is stable. You should have all of your tests pass (unless you're intentionally creating failed tests) when your application and data is working properly.

Writing a unit test when the app is having a problem is very risky because it can give you false positives, but it can be done if you know exactly what you should expect out of your app.

What Unit Testing doesn't do for you


This seems to be the area where students struggle the most with Unit Testing, so here are the main 2 things Unit Testing does not do for you:

1. Unit testing can help you find a problem, and it can do it very fast and easy. However, it will not tell you how to fix the problem.
But think about it, in the example above, what would the solution be?
Should we modify our app?
Should we complain to the server to provide us with valid data?
Should the server remove that problem object?

In every situation the solution would be different, so Unit Testing can't tell you how to fix this.

2. Unit testing will not create any new end-user functionality to your app. Unit testing is basically a tool within an app created by developers for developers. This means that you can write unit testing cases for your app for a whole month, and your users won't see anything new (except possibly less issues). This gives new developers a feeling of writing code for nothing.

So there you have it. I hope a real-world example helps you.

Yes there could also be a better debugging environment, but the exaggerated scenario described here is very possible and it should highlight the benefits of Unit Testing.

Please let me know if you still have questions about Unit Testing, or provide any comments you may have about the concept of Unit Testing.

No comments:

Post a Comment