Monday, February 13, 2017

Unit testing (with JUnit) and some emphasis on mock objects

A note ahead. This post is not about how JUnit works, so do not expect to get any detailed knowledge of JUnit itself. It's more about why we should do unit testing, what it is, what it is not. And we'll also try to shed some light on mock objects. 


Unit testing has long been a standard in software development. It's an integral part of any serious, professionally developed project. Just try to download any widely accepted project from GitHub and you'll most probably see that it's got its separate test folder that contains more or less the same number of unit test files as that for the real classes. So don't expect to be counted a serious developer if you plan to skip unit tests on your projects. Following are the main headlines that I'll be talking about in this post:

  1. What really unit testing is
  2. What unit testing is not
  3. What mock objects ARE, honestly

What is unit testing

Consider the following two classes:


public class Automobile {
    private Location currentLocation;

    public Location getCurrentLocation() {
        return currentLocation;
    }

    public void setCurrentLocation(Location location) {
        this.travelledSoFar += this.currentLocation.getDifferenceInMiles(location);
        this.currentLocation = location;
    }

    private float remainingFuel;

    public float getRemainingFuel() {
        return remainingFuel;
    }

    public void setRemainingFuel(float remainingFuel){
        if (remainingFuel > this.tankCapacity)
            this.remainingFuel = tankCapacity;
        else
            this.remainingFuel = remainingFuel;
    }

    private float travelledSoFar;

    public float getTravelledSoFar() {
        return travelledSoFar;
    }

    private final float tankCapacity;
    private final float mpg;

    public Automobile(float tankCapacity, float mpg) {
        this.tankCapacity = tankCapacity;
        this.mpg = mpg;
        this.currentLocation = new Location(0f, 0f);
    }

    public Automobile() {
        this(17f, 20f);
    }

    //Takes from current location to target location
    public void drive(Location target) {
        //What if we pass null for target? :)
        Location initialLocation = this.currentLocation;
        this.currentLocation = target;
        float distanceTravelled = this.currentLocation.getDifferenceInMiles(initialLocation);
        this.travelledSoFar += distanceTravelled;
        reduceRemainingFuel(distanceTravelled);
    }

    //Refuels with the supplied gallons of fuel
    public void refuel(float gallons) throws FuelAmountExceedingTankCapacityException {
        float amountAfterRefuel = this.remainingFuel + gallons;
        if (amountAfterRefuel > this.tankCapacity)
            throw new FuelAmountExceedingTankCapacityException(this.tankCapacity, amountAfterRefuel);
        this.remainingFuel += gallons;
    }

    private void reduceRemainingFuel(float travelledDistance) {
        this.remainingFuel -= travelledDistance / this.mpg;
    }
}


class Location {
    private float x;

    private float y;

    public float getX() {
        return this.x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public float getDifferenceInMiles(Location anotherLocation) {
        return Math.abs(this.x - anotherLocation.x);
    }

    public Location(float x, float y) {
        this.x = x;
        this.y = y;
    }
}

The unit we're interested in testing is Automobile class. It's got some functionalities. We need to make sure that all the expectations from an Automobile are met. Ideally and in fact normally we should test all the public methods from as many different aspects as possible. So we start to ask these questions:

    The method void drive(Location targetLocation)
  • When we are at location A and want to drive to location B, does it really end up in B?
  • Does it update the mileage as it drives?
  • What if we say drive but don't specify any target location? Does it reset the mileage to 0?
  • Does the remaining fuel reduce during the trip?
    The method void refuel(float gallons)
  • Does it refuel exactly the same amount?
  • Is it protected against overfilling?

In fact, there are so many more questions to ask, like "What if you don't have enough fuel for that trip?" but for the sake of brevity, I'll skip many of them. So with these questions in mind, we go and set up our unit tests that will check if those intended behaviors are demonstrated by that unit. 

BE PATIENT AND DO NOT WORRY. I WILL EXPLAIN THE BENEFITS OF UNIT TESTING. I STILL REMEMBER THAT IT'S NOT A POST ABOUT HOW UNIT TESTING IS DONE IN JUNIT AND THAT I HAVE TO EXPLAIN WHY WE SHOULD DO UNIT TESTING. SO BEAR WITH ME.


import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 * Class for unit testing Automobile class
 */
public class AutomobileTest {
    Location target;
    Automobile auto;

    @Before
    public void setUp() {
        target = new Location(100, 0);
        auto = new Automobile(20, 21);
    }

    @Test
    public void currentLocationIsCorrectAfterDrive() {
        auto.drive(target);
        assertTrue(auto.getCurrentLocation().getX() == 100);
    }
    @Test
    public void distanceTravelledSoFarEquals170() {
        Location location=new Location(80,40);
        auto.setCurrentLocation(location);
        auto.drive(target);
        target=new Location(30,70);
        auto.drive(target);
        assertTrue(auto.getTravelledSoFar()==170);
    }

    @Test
    public void remainsFiveGallonsOfFuelAfterTravelling210Miles()throws FuelAmountExceedingTankCapacityException{
        auto.setRemainingFuel(15);
        target.setX(210);
        auto.drive(target);
        //Mpg is 21, so after driving 250 miles, the tank should have 15-210/21=5.0f
        assertTrue(auto.getRemainingFuel()==5f);

    }
}

Here we've taken the drive method and set up some tests on its behavior. First, we wanted to see if it really drives to the given location. So we invoked that method and checked the location of the car after the ride. Then there's a second test that checks if the odometer works. For that, we prepared the car and target location with some test values and checked the odometer. And then we set up yet another test to see if (correct amount of) fuel is used, again by filling up the tank, driving and looking into the tank. We'll get this beautiful, all-green output if we run all tests:






Now you might ask why we need unit test when we can test any behavior by simply calling any method we're interested in with some test values and assert the correctness. Well, that's right and with a unit testing frameworks you do exactly the same but

  1. Although you can have your unit tests mixed with your source code, they're normally kept separate. So you don't have to clean the mess left after each time you want to make sure your code works as expected. Just think about how many classes and methods you can have in an average program. 
  2. Unit tests can be automated. You once set them up and make sure they're run during project builds. Do not confuse unit testing with compilation. It does not check whether your code is valid or not. It checks whether your code behaves as you want it to. And running your units is the only way to assert that. It's like a team of testers that test your code manually right after compilation by providing some probe values and calling your methods in many different contexts. They automatically do all the tests at the point you ask them to. Again, unit testing is not a compilation level test, it's a series of ordinary, predefined methods that eventually invoke methods on units(usually classes) with some test values or conditions. 
  3. They never miss anything. They make sure that all the behavior tests are conducted. But we are human beings and are prone to forget. We might change the way a method in one unit works, which can affect the behavior of another unit but forget to test the dependent unit. I'll expand on this a minute later
  4. Well, if you still say you can make your own unit testing framework, then go ahead.  
Now, let's take a look at the following method in the Location class:


    public float getDifferenceInMiles(Location anotherLocation) {
        return Math.abs(this.x - anotherLocation.x);
    }

It simply returns the absolute value of the difference between the x values of itself and the location passed. What if change the body of the method to this?:


    public float getDifferenceInMiles(Location anotherLocation) {
        return (float) Math.abs(Math.hypot(this.x - anotherLocation.x,
                                                         this.y-anotherLocation.y));

Well, the compilation will still succeed. But wait a minute. Our Automobile class seems to heavily depend on this method. Without unit testing in place, we can easily forget that the behavior of our drive method will change and won't work as expected. But with unit testing, we'll get a failure on the test named distanceTravelledSoFarEquals170. Here's the output if we run the test this way.






With unit testing, you make sure that the behaviors you've set up a test for are guarded against any change. Not that they will recover from the situation, but they will inform you that something's wrong with the way your method should work. Unit testing frameworks, in our case Junit, will give a list of passed and failed tests so you can concentrate on the failed ones.

Besides all these benefits, unit testing perfectly describes what your unit is built to do. So anyone, including yourself, looking at the test cases will understand the purpose of the unit better. 

And one more fantastic benefit: It'll force you to design your API better by making you abstract it away. You'll notice that your code becomes more cohesive and decoupled as you try to make it better testable. It turns out unit testing has brought lots of good side effects although the main intention was to test the correctness of behavior.


What unit testing is not


  • It's not integration test.As the name implies, unit testing is to test a unit of work at its own level. Integration test is the process where you test whether different components work together well. 
  • It's not meant for testing other people's code. You test how your piece of code behaves under certain circumstances. In fact, you can conclude that some other dependency that your unit depends on is buggy by testing your own code. Honestly, what can you do if Math.hypot method does not work right?
  • Unit testing is not meant to replace the process of testing your software as a whole project. You still need to run it and see if it works as expected. And it's definitely not to replace the functional testing phase. 
  • It's not a waste of time. Despite the fact that you spend a decent amount of time on writing unit tests, that pays off very quickly. Think about a car manufacturing plant. Before production, you build the automation process. That takes some time. But then you sit and relax while the robots build cars. I know it's not exactly the same but the idea behind time spending is the same.

What are mock objects, honestly

As I mentioned earlier, unit testing is focused on a unit of work. We just test one functionality by creating test conditions. But sometimes we depend on other objects that affect how our code works. That dependency might behave differently at various circumstances and we need to make sure that our System Under Test (SUT, that's one of the several ways of calling our unit that we want to test) acts accordingly. Mocks are one of the ways of getting the dependency to do what you want. They are just skeletons of the classes. They have no functionality. Let's say your automobile class depends on another class called GpsNavigator. You don't always get the same route to a location. The result depends on many factors like traffic, weather etc. So to be able to see if the automobile drives as expected, we need to make the GPS navigator do and return just want we want it to. Assume the GpsNavigator class looks something like this:

    public class GpsNavigator{
      public Route getRouteForAddress(String address){
         return doSomeMagicAndGetMeTheRouteToAddress(address);
      }
    }

If that doSomeMagicAndGetMeAddress method is not guaranteed to return the same value for the same input each time you invoke it, then you won't be able to build your assumptions on that. For that same reason, we need to be able to control this. Take a look at the below code. We use Mockito for this post.


   //some other imports
   ...
   import static org.mockito.Mockito.*;
   public class AutomobileTest{
     final GpsNavigator gpsMock = mock(GpsNavigator.class);
     Automobile auto=new Automibile(gpsMock);
     
     @Test
      public void remainingFuelIs5GallonsAfterDrivingFromZeroToCupertiono(){
          Route routeThatNeeds10GallonsOfFuel=RouteHelper.getRouteForGallons(10);         
          when(gpsMock.getRouteForAddress("Cupertino")).thenReturn(routeThatNeeds10GallonsOfFuel);
          auto.setRemaningFuel=15f;
          auto.driveByGps("Cupertino");
          assertTrue(auto.getRemainingFuel.equals(5f));
      }
     
   }

  1. We created a mock object of GpsNavigator class.
  2. We passed that object to our SUT, the automobile class via its constructor. We have to design our automobile class so that we can inject its dependencies externally. Constructors are one way of doing this. We do this because we want to make sure that during the test the automobile class uses our mock object, instead of a real one.
  3. Then inside the test method, we instruct our mock object to always return the particular Route (in this case the one that needs 10 gallons of fuel) whenever we need to go to Cupertino.
  4. We make sure that we have 15 gallons of fuel in the tank
  5. We drive to Cupertino
  6. Finally, we assert that the remaining amount of fuel is 5 gallons
Now, what do you think the gpsMock object would return if we omitted this line?

when(gpsMock.getRouteForAddress("Cupertino")).thenReturn(routeThatNeeds10GallonsOfFuel);

It would return null. Why? Because method bodies of mock objects are empty. To be more precise, their bodies either contain nothing between two curly braces or return the default value of the return type of the method. In other words, they lack the real implementation. This is how the bodies of the methods in the GpsNavigator class looks like when we mock it:


public class GpsNavigator{
   public Route getRouteForAddress(String){
     return null;
   }
   public boolean hasArrivedToDestination(){
     return false;
   }
   private void calculateTheMostEfficientRoute(){
     //nothing here :)
   }  
}



How Mockito does this is beyond the scope of this post. If you're really interested, you can learn about CGLIB or you can go directly to the Mockito source.






Thursday, January 26, 2017

Slapping in the face of checked vs unchecked exceptions confusion

I've come across lots of people finding it damn difficult to understand the difference and purpose of checked and unchecked exceptions in Java. In fact, I used to feel the same about this. So I decided to contribute my two cents in hopes it helps anyone. 

To make it easy to understand,I'll first talk very briefly about "catch or throw" rule that we have for exceptions in Java. In fact, this rule and the concept of checked and unchecked exceptions are so mutually dependent, that it is a little hard to decide which one to explain first. So I picked up this rule, as no matter which one I start with, eventually everything's going to be clear when connecting the ends.

Consider the following method that throws FileNotFoundException when, well when it cannot find the specified file: 




    void printFileContent(String fileName)throws FileNotFoundException,
                                                                IOException{
        FileInputStream fis = new FileInputStream(fileName);
        int read;
        while((read = fis.read() ) != -1)
        {
            System.out.print((char)read);
        }
    }


The programmer that coded the above method did nothing wrong. FileInputStream constructor takes a file name and it tries to build a FileInputStream object based on that file. When it can't find the file, it throws the self descriptive exception called FileNotFoundException. The only thing he/she could've done was to either absorb the exception or throw it to the caller of the printFileContent method. As you might've guessed, absorbing the exception internally without ever letting know the caller method what happened would make no sense, so it's better to throw the exception such that the caller knows that the file does not exist. Because it's the caller who is responsible for providing a valid, existing file name. 

BTW, do you see a redundant piece of code in the above method? The method rethrows 2 exceptions: FileNotFoundException, which is thrown by FileInputStream constructor and IOException, which is thrown by the read method of the FileInputStream class. In fact, we didn't need to specify FileNotFoundException in the throws section, because it's a subclass of IOException. And you probably know that we can specify super class for all child exception types. The same applies for catch. Just core inheritance case.

Now, if I were to neither throw, nor catch the FileNotFoundException, I would've been warned by the compiler, thus the compilation would fail. Why? Because FileNotFoundException as well as the IOException are checked exceptions. This does not mean that there's some kind of attribute attached to them denoting them as checked. No. Why they are called checked is because compiler checks whether the "catch or throw" rule has been applied to them. But how does compiler know which exceptions to check for? Well, it's quite straight forward:

Compiler skips all exceptions that descend from RuntimeException, which therefore are called unchecked exceptions. Repeat with me: All RuntimeExceptions are unchecked exceptions and all unchecked exceptions are RuntimeExceptions. That simple. 

So checked exceptions are the ones that are not RuntimeException. And why they're called checked is because compiler checks the "catch or throw" rule on them. To make final definition on this: This categorization of checked and unchecked is in terms of compiler check. 

But still you might be confused by this thought: FileNotFoundException is also thrown in runtime anyway. And in fact any exception is thrown during runtime. There's no exception that can be thrown without running the container program. Why are exceptions like NullPointerException, ArrayIndexOutOfBoundsException,ArithmeticException subclasses of RuntimException when IOException is not? Well, I might agree that the roof name RuntimeException was not the best one to choose. To answer this, let's assume that there's no exception class called RuntimeException and James Gosling, Mike Sheridan and Patrick Naughton are in the middle of the process of designing the Java language. Mike has a great idea to make programmers be aware of possible exceptions. If they're aware, then they will decide what to do with them when they get one. They will either handle it or they will throw it to the caller of their methods. But  they will be aware of them. James finds this idea to be great but he suggests to skip those exceptions that are caused by bad programming. He thinks a programmer should be concerned only about those exceptions that can be caused by natural events, such as when trying to connect to a database and the database is down, or when reading from network socket and suddenly some switch malfunctions, or when trying to open a file that has recently been removed etc. But he/she shouldn't be concerned about the exception that might be thrown when code inside the method he/she calls invokes a method on some object that might not have been properly instantiated. So they agree on this: Let's collect those exceptions that are result of bad programming under a class called, ummm.....What about RuntimeException. Yes, let's call it RuntimeException and make the compiler check all exceptions except those that are RuntimeException.

Now, take a look at the following examples of bad programming practice:



    String trimAnySpace(String str){
        return str.replace(" ","");
    }

This tiny code looks very naive but there's a flaw in it. What if the caller of this method decides to pass null for the str ? That's correct, the NullPointerException. The programmer must first check if the passed parameter is a valid object. Here's one of several possible solutions

    String trimAnySpace(String str){

       if(str!=null)
        return str.replace(" ","");
       return str;

    }
Or you could throw another exception that would let the caller know the passing null argument is not allowed.

Here's another buggy piece of code:
    int divideFirstElementWithDivisor(int[] numbers, int divisor){
        return numbers[0]/divisor;
    }

Please, take a look at this one. Just one line of code can potentially produce 3 different exceptions.

  1. What if the numbers is passed a null value? Answer is NullPointerException.
  2. What if the numbers array is empty? Answer is ArrayIndexOutOfBoundsException.
  3. What if the divisor is passed 0? Answer is ArithmeticException.
So to conclude. Checked and unchecked exception concept was essentially built to help programmers be aware of possible exceptions(again, the natural exceptions). They do nothing different. What you can do with checked ones, you can also do with the unchecked ones and other way around. And unchecked exceptions are in fact the ones that are outside of the set of checked exceptions. There are unchecked exceptions because there are checked exceptions. It's like the movie Terminator. There's no Terminator 1. There is Terminator 2 because there is a movie called Terminator. Hadn't there been Terminator, there wouldn't have been Terminator 2 either. Phew.