Test-Driven Development with Mockito

3.5 (2 reviews total)
By Sujoy Acharya
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies

About this book

The usual life cycle of code involves adding code, breaking an existing functionality, fixing that and breaking a new area! This fragility can be fixed using automated tests and Test Driven Development.

TDD’s test first approach expedites the development process and unit tests act as safety nets for code refactoring and help in maintaining and extending the code. This makes TDD highly beneficial for new projects.

This practical, hands-on guide provides you with a number of clear, step-by-step exercises that will help you to take advantage of the real power that is behind Test Driven Development and the Mockito framework. By using this book, you will gain the knowledge that you need to use the Mockito framework in your project.

This book explains the concept of Test Driven Development (TDD), including mocking and refactoring, as well as breaking down the mystery and confusion that surrounds the test first approach of TDD. It will take you through a number of clear, practical examples that will help you to take advantage of TDD with the Mockito framework, quickly and painlessly.

You will learn how to write unit tests, refactor code and remove code smells. We will also take a look at mock objects and learn to use Mockito framework to stub, mock, verify and spy objects for testability. You will also learn to write clean, maintainable, and extensible code using design principles and patterns.

If you want to take advantage of using Test Driven Development and learn about mocking frameworks, then this is the book for you. You will learn everything you need to know to apply Test Driven Development in a real life project, as well as how to refactor legacy code and write quality code using design patterns.

Publication date:
November 2013
Publisher
Packt
Pages
172
ISBN
9781783283293

 

Chapter 1. Getting Familiar with TDD

Test-Driven Development (TDD) is an evolutionary development approach. It offers test-first development where the production code is written only to satisfy a test and refactor.

In this chapter we will look at the following topics:

  • Definition of tests

  • Examples of TDD

  • The big picture

  • Steps of TDD

 

Definition of test


We all go through class tests and medical tests; musicians often check musical instruments before the program. A test is an assessment of our knowledge, a proof of concept, or an examination of data.

A class test is an examination of our knowledge to ascertain whether we can go to the next level. For software, it is the validation of functional and non-functional requirements before it is shipped to customers.

Like other things, Java code can be unit tested using a code-driven unit-testing framework.

The following are a few of the available code-driven unit-testing frameworks for Java:

  • SpryTest

  • Jtest

  • JUnit framework (junit.org)

  • TestNG

JUnit is the most popular and widely used unit-testing framework for Java.

In this book, we will be using JUnit 4.0 for unit testing code.

To learn more about JUnit framework, refer to Appendix A, TDD Tools and Frameworks.

Object-function programming languages, such as Scala and Groovy, are becoming very popular. They are intended to be compiled as bytecode, and executable code can be run on JVM. Also, they can access Java libraries. Scala/Groovy helps in writing brief, useful tests.

Using Scalatest (http://www.scalatest.org/), you can unit test Scala code as well as Java code.

Usually, developers unit test the code using the main method or by executing the application. Neither of them is the correct approach. Mixing up production code with tests is not a good practice. It creates a code maintainability problem. The best approach is to create a separate source folder for unit tests and put the test class in the same package as the main class. Usually, if a class name is TaxCalculator, its test should have the name TaxCalculatorTest.

Let us write a program to calculate tax for the FY2012-13 and test it.

The rules are as follows:

  • If the taxable income is less than USD 500,000, then deduct 10 percent as tax

  • The tax is 20 percent for taxable income between USD 500,000 and USD 1,000,000

  • The tax is 30 percent for taxable income above USD 1,000,000

So, let us look into the steps that are as follows:

  1. Launch Eclipse and create a Java project TDD_With_Mockito. By default, Eclipse will create an empty project with a source folder src. We will add packages and Java files under the src source folder. But, as mentioned in the preceding section, the best practice is to have a separate source folder for unit tests. We will add all our unit tests under the test source folder.

  2. To create a source folder test, right-click on the project. Eclipse will open a pop-up menu. Expand the New menu and click on the Source Folder menu item. It will open the New Source Folder pop-up. In the Folder Name textbox, enter test and hit the Finish button. It will add the test source folder.

  3. Now add a new JUnit 4.0 test TaxCalculatorTest in the test folder. To execute unit tests, Eclipse needs the JUnit library in the classpath, so Eclipse will recommend adding the JUnit 4.0 library to the classpath. You can accept the recommendation or manually add the JUnit library to the classpath.

    Add a test to verify the first rule that the tax is 10 percent when the taxable income is less than USD 500,000.

  4. Create a public void method when_income_less_than_5Lacs_then_deducts_10_percent_tax () in TaxCalculatorTest.java. Annotate the method with the @test annotation. JUnit 4.0 needs this annotation to identify a test:

    @Test
    public void when_income_less_than_5Lacs_then_deducts_10_percent_tax() {
      
    }

    For tests, we will follow the convention: when_some_condition is met_then_this_happens.

    We will use underscores (underscore in a method name is not recommended for production code) for test methods.

  5. In this test method, write new TaxCalculator(). The compiler will complain that the class doesn't exist. Press Ctrl + 1; Eclipse will suggest the creation of a TaxCalculator class. Select the option and create the class in the com.edu.chapter01 package under the src source folder.

  6. We have the calculator class ready! Now we need to add a method that will take the total taxable income as an input and return the payable tax amount. Write double payableTax = taxCalculator.calculate(400000.00); and the compiler will tell you that this method doesn't exist. Press Ctrl+1 and select Create method calculate(double) in type 'TaxCalculator'.

  7. Our API is ready. We need to verify that this method returns 10 percent of 400,000. To do that, we have to take the help of the JUnit framework. We will assert the expected and actual values. Write assertTrue(40000 == payableTax);.

    Note

    The org.junit.Assert class provides a set of static assertion methods. We can statically import any method we need.

    assertEquals(expected, actual) is a method that takes two values: expected and actual. If actual calculated value doesn't match the expected value, it throws an exception and the test fails. It indicates that there is something wrong in the calculation.

    We should have used this method here. But assertEquals is deprecated for double values. So, to verify an expected double, we will not use this deprecated method. For any other data type, assertEquals is the best choice.

    We can use BigDecimal instead of the primitive double. BigDecimal is recommended for double value calculation, such as subtraction and multiplication.

  8. Run the test (press Alt + Shift + X then T). The test will fail. The calculate() method returns 0. Open the TaxCalculator class and change the calculate() method to return 40,000, that is, 10 percent of USD 400,000. Save the file and run the test. Bingo! It works. The following is the test:

    @Test
    public void when_income_less_than_5Lacs_then_deducts_10_percent_tax() {
      TaxCalculator taxCalculator = new TaxCalculator();
      double payableTax = taxCalculator.calculate(400000.00);
      assertTrue(40000 == payableTax);
    }

    Following is the code snippet:

    public class TaxCalculator {
      public double calculate(double taxableIncome) {
        return 40000;
      }
    
    }
  9. Now check the boundary values 0 and 500,000. Modify the test method and call the calculate method with 0 and 500,000.

    @Test
    public void when_income_less_than_5Lacs_then_deducts_10_percent_tax() {
      TaxCalculator taxCalculator = new TaxCalculator();
      double payableTax = taxCalculator.calculate(400000.00);
      assertTrue(40000 == payableTax);
    
      payableTax = taxCalculator.calculate(0);
      assertTrue(0 == payableTax);
    
      payableTax = taxCalculator.calculate(500000.00);
      assertTrue(50000 == payableTax);
    
    }

    Run the test.

    The test fails because it expects 0, but the actual method returns 40,000. If we return 0 from calculate(), it will fail the 40,000 data condition; if we return 50,000, then it will fail 40,000 and 0, that is, both conditions. So for the three test conditions, we need three values—40,000, 0, and 50,000. Returning 40,000 from the calculate method causes this test failure. It seems we need to return 10 percent of the taxable income. Add this condition to our calculate() method.

    The code will look something like this:

    public class TaxCalculator {
      public double calculate(double taxableIncome) {
        return (taxableIncome / 10);
      }
    
    }

    Rerun the test. It will pass. So we covered a random value and two boundary value conditions.

  10. Now, write another test and enter any amount greater than 500,000 as an input to the calculate() method. Oops! The test fails! We need to return 20 percent above 500,000 and 10 percent below USD 500,000. Change the code to return 20 percent of USD 300,000 and 10 percent of USD 500,000.

    @Test 
    public void when_income_between_5lacs_and_10lacs_then_deducts_fifty_thousand_plus_20_percent_above_5lacs() {
      TaxCalculator taxCalculator = new TaxCalculator();
      double payableTax = taxCalculator.calculate(800000.00);
      double expectedTaxForFirstFiveHundredThousand = 50000;
      double expectedTaxForReminder = 60000;
      double expectedTotalTax = expectedTaxForFirstFiveHundredThousand + expectedTaxForReminder;
      assertTrue(expectedTotalTax  == payableTax);
    }

    Change the calculate() method to return 110,000:

    public class TaxCalculator {
      public double calculate(double taxableIncome) {
        return 110000;
      }
    }

    Our new test runs fine, but the existing tests are broken. So, reverse the change. We need to return 10 percent of the taxable income when the amount is less than 500,000, otherwise the test will return 110,000.

    public class TaxCalculator {
      public double calculate(double taxableIncome) {
        if(taxableIncome > 500000){
          return 110000;
        }
        return (taxableIncome / 10);
      }
    }
  11. Everything is green. All tests are proving the concept. It's time to test some other value. We will try USD 900,000. The test will fail since it doesn't get the expected value; instead calculate returns 110,000. We need to add code to return 20 percent above USD 500,000

    public class TaxCalculator {
      public double calculate(double taxableIncome) {
        if(taxableIncome > 500000){
          return 50000+((taxableIncome-500000)/5);
        }
        return (taxableIncome / 10);
      }
    }

    Yes, it is working for both the tests! Add another test to work with income greater than USD 1,000,000.

What we just completed is TDD.

Kent Beck is the originator of Extreme Programming and TDD. He has authored many books and papers. Please visit the following link for details:

http://en.wikipedia.org/wiki/Kent_Beck

TDD gives us the following benefits:

  • Clean, testable, and maintainable code.

  • Another benefit to incrementally building your code is that your API is easier to work with, because the code is written and used at the same time.

  • When we document our code, and then update the code but forget to update the documentation, it creates confusion. You can document your code and keep it updated or write your code and unit tests in such a way that anybody can understand the intent. In TDD, tests are written to provide enough documentation of code. So, the test is our documentation, but we need to clean the tests too in order to keep them readable and maintainable.

  • We can write many tests with boundary value conditions, zero, negative numbers, and so on, and verify our code. You are trying to break your own code as soon as possible. There is no need to package the whole application and ship it to quality assurance (QA) or the customer to discover issues.

  • You should also avoid over engineering the class that you are writing. Just write what's needed to make all tests green.

 

The big picture


It doesn't matter how small or big a project is; every project has an architecture. An architect (or designer) takes design decisions to satisfy the functional goal of the project and normalizes non-functional requirements, such as security, availability, performance, and scalability. In this process, different components of the system and interaction between these components are identified.

For example, health provider organizations (hospitals) provide care 24/7, so the patient check-in software needs to be available at all times. Also, it needs to communicate with insurance companies to validate policy information, send claims, and receive remittances. Here, the architecture should define the different components of the system, the protocol to communicate with insurance companies, and how to deploy the system so that it complies 24/7.

For testing architecture, unless the code is ready for testing, you cannot test quality attributes and functionality. What if during testing we find the communication protocol we defined is wrong? All the effort of architecture and coding is wasted.

It is not wise to put months of effort in the architecture; cards, sequence diagrams, and models are essential to represent the architecture, but only drawing conceptual diagrams doesn't help. Before the construction phase, the baseline architecture needs to be provided to the development team. While building this baseline architecture, write testable code, identify problems, and attack them quickly. If anything goes wrong, that can be fixed early.

We all often have trouble believing our own code is broken. In the traditional waterfall approach, the developers write the code and pass the completed development to the software testers. The testers try to break the system and find bugs!

TDD helps a lot here. The flow is the reverse of conventional flow and iterative.

The following are the steps involved in TDD:

A test is written for a requirement, a code is added to pass the test, the code is cleaned, and the design is identified. Then again another test is added; this process continues.

 

Refactoring


The third step of TDD is refactoring. Let us revisit the code we wrote for TaxCalculator:

public class TaxCalculator {

  public double calculate(double taxableIncome) {
    if(taxableIncome > 1000000){
      return 150000 + (((taxableIncome-1000000)*30)/100);
    }

    if(taxableIncome > 500000){
      return 50000+((taxableIncome-500000)/5);
    }

    return (taxableIncome / 10);
  }

}

The tests are running fine but the code looks ugly. It is not readable. What is 150000 + (((taxableIncome-1000000)*30)/100)? Extract methods to explain the intent of the code. Create two methods isIncomeIn30PercentTaxRange(…) and isIncomeIn20PercentTaxRange(..) to check the amount greater than 1,000,000 and 500,000 respectively. Replace conditions with methods. And rerun the tests. If any test fails, then immediately reverse the code. Change and identify the root cause.

public double calculate(double taxableIncome) {
  if(isIncomeIn30PercentTaxRange(taxableIncome)){
    return 150000 + (((taxableIncome-1000000)*30)/100);
  }

  if(isIncomeIn20PercentTaxRange(taxableIncome)){
    return 50000+((taxableIncome-500000)/5);
  }

  return (taxableIncome * .10);
}

Still not readable! When taxable income is more than 1,000,000, deduct 30 percent above 1,000,000 and for an amount less than 1,000,000, apply another strategy. This can be represented using the following code:

public double calculate(double taxableIncome) {
  if(isIncomeIn30PercentTaxRange(taxableIncome)){
    return deduct30PercentAbove10Lacs(taxableIncome) + calculate(1000000);
  }

  if(isIncomeIn20PercentTaxRange(taxableIncome)){
    return deduct20PercentAbove5Lacs(taxableIncome) + calculate(500000);
  }

  return (taxableIncome * .10);
}
protected double deduct20PercentAbove5Lacs(double taxableIncome) {
  return (taxableIncome-500000)*.20;
}

protected double deduct30PercentAbove10Lacs(double taxableIncome) {
  return (taxableIncome-1000000)*.30;
}

Now the code is more readable and maintainable. This process is known as Refactoring. We will explore more of this in the next chapter.

 

Summary


In this chapter we read about TDD, practiced test-first development, and covered the TDD big picture.

By now, the reader will be able to understand the core concept of TDD.

Chapter 2, Refactoring – Roll the Dice, focuses on getting the reader quickly started with code refactoring and code smells.

About the Author

  • Sujoy Acharya

    Sujoy Acharya works as a Principal Engineer with Cerner. While growing up, he pursued his interests in the fields of computer science and engineering. His hobbies are watching movies and sitcoms, playing outdoor sports, and reading books.

    Sujoy likes to research upcoming technologies. His major contributions are in the fields of TDD, building scalable applications, cloud services, and the Spring Framework.

    He has authored four books for Packt, namely, Test-Driven Development with Mockito, Mastering Unit Testing using Mockito and JUnit, Mockito Essentials, and Mockito for Spring.

    Browse publications by this author

Latest Reviews

(2 reviews total)
ok
Excellent
Book Title
Access this book, plus 7,500 other titles for FREE
Access now