Categories: Unit Testing

Test-Driven Development (TDD) Shines with Mocking

The article presents a perspective and some code samples on how one could some cool stuff with Test-driven development (TDD) and Mocking. The code samples are done in Java.

Lets briefly understand what is TDD and mocking?

What is TDD?

Test-driven development, simply speaking, is a software development process in which developers write tests first and, then writing enough code to pass those tests. Once all of the tests pass, they do code refactoring to enhance code quality. Following are key advantages of adopting TDD as your development process:

  1. Enforces the developers to thoroughly think through various different test cases which could be used to test the functional/business requirements. It, thus, takes care of lot of bugs that could be found during QA and post-production phases which are very difficult to fix.
  2. Developers get to work very closely with both, business analyst and test engineers. This propagates team work.
  3. Code refactoring can be done in easy way.

Following diagram represents TDD process:

Test-driven development (Courtesy: Wikipedia)

Following are some good pages on the web to get yourself going with TDD:

 

What is Mocking?

Mocking is a unit testing phenomenon which helps to test objects in isolation by replacing dependent objects with complex behavior with test objects with pre-defined/simulated behavior. These test objects are called as Mock objects.

 

How does TDD shine with Mocking?

In TDD, the primary objective is to pass the tests that are written first. However, in real world scenario, as one starts writing the code, one come across various cases which could be made as dependent classes. Following are different options one can do:

  • Without Mocking: Write all code in the same class and pass the tests. Once the tests pass, refactor the code by extracting methods and classes. Make sure the tests pass.
  • With Mocking: Instead of requiring to write all the code at once in the same class, just implement the flow by making use of dependent classes and make sure the tests pass. In doing this, just define the dependent classes,  and mock them in unit tests. By doing this, one would ensure that method flow pass without having the need to write the entire code. The advantage of using mocking is the interesting behaviors that one could simulate without having need for the code. This is demonstrated later in this article with code samples. Following diagram demonstrates the fact that with mocking, one may not need to write the dependent classes and still complete the code flow while making sure all the related tests pass.

 

Software Requirement

We will try and see how TDD shines with mocking with this requirement. The requirement is to add a new restaurant to a restaurant database system. One should be able to add a restaurant if the mandatory data are entered.

 

Unit Test Written First

Looking at the requirements, one could come up with some of the following tests:

  1. restaurantNotCreatedDueToValidationFailure
  2. restaurantNotCreatedDueToPersistenceFailure
  3. restaurantCreated

Following is how the unit test (written first) looks like:

Unit Test: NewRestaurantTest.java

public class NewRestaurantTest {

 @Before
 public void setUp() throws Exception {  
 }
 @After
 public void tearDown() throws Exception {
 }
 @Test
 public void restaurantCreated() {  
 }
 @Test
 public void restaurantNotCreatedDueToValidationFailure() {  
 }
 @Test
 public void restaurantNotCreatedDueToPersistenceFailure() { 
 }

}

 

Class Design

Looking at above tests, following different classes came into picture:

  1. NewRestaurant: Comprising of logic to create the restaurant
  2. Restaurant: Domain object representing the restaurant
  3. RestaurantValidation: Consisting of validation logic matching the business rules required to add a new restaurant
  4. RestaurantDAO: Consisting of logic to persist restaurant object into database

Java Class: NewRestaurant.java

Note the createRestaurant method in which validated and persist methods are called on classes RestaurantValidation and RestaurantDAO respectively. These methods are, however, empty methods in their respective classes. However, with these methods, the flow for creating the restaurant is complete. Look at the code below.

public Restaurant createRestaurant( Restaurant restaurant ) {
  Restaurant retRest = null;
  if( resVal.validated( restaurant ) ) {
   if( resDAO.persist( restaurant ) ) {
    retRest = new Restaurant( restaurant );
   }
  }
  return retRest;
 }

To test the above code, following code demonstrates the usage of mocking. Note how validated and persist methods on RestaurantValidation and RestaurantDAO respectively are mocked in three different tests shown below.

Unit Test: NewRestaurantTest.java

@Test
 public void restaurantCreated() {
  Restaurant restaurant = new Restaurant();
  Mockito.when( restVal.validated(restaurant)).thenReturn( true );
  Mockito.when( restDAO.persist(restaurant)).thenReturn( true );

  Restaurant newRest = newRestaurant.createRestaurant(restaurant);
  Mockito.verify( restVal ).validated(restaurant);
  Mockito.verify( restDAO ).persist(restaurant);
  assertNotNull( newRest );
 }

 @Test
 public void restaurantNotCreatedDueToValidationFailure() {
  Restaurant restaurant = new Restaurant();
  Mockito.when( restVal.validated(restaurant)).thenReturn( false );
  Mockito.when( restDAO.persist(restaurant)).thenReturn( true );

  Restaurant newRest = newRestaurant.createRestaurant(restaurant);
  Mockito.verify( restVal ).validated(restaurant);
  Mockito.verify( restDAO, Mockito.never() ).persist(restaurant);
  assertNull( newRest );
 }

 @Test
 public void restaurantNotCreatedDueToPersistenceFailure() {
  Restaurant restaurant = new Restaurant();
  Mockito.when( restVal.validated(restaurant)).thenReturn( true );
  Mockito.when( restDAO.persist(restaurant)).thenReturn( false );

  Restaurant newRest = newRestaurant.createRestaurant(restaurant);
  Mockito.verify( restVal ).validated(restaurant);
  Mockito.verify( restDAO ).persist(restaurant);
  assertNull( newRest );
 }

Thus, as a summary, if you would want to have greater fun with TDD, make use of mocking as it allows you to simulate some interesting behaviors without the need for writing any code.

[adsenseyu1]

Ajitesh Kumar

I have been recently working in the area of Data analytics including Data Science and Machine Learning / Deep Learning. I am also passionate about different technologies including programming languages such as Java/JEE, Javascript, Python, R, Julia, etc, and technologies such as Blockchain, mobile computing, cloud-native technologies, application security, cloud computing platforms, big data, etc. I would love to connect with you on Linkedin. Check out my latest book titled as First Principles Thinking: Building winning products using first principles thinking.

Share
Published by
Ajitesh Kumar

Recent Posts

Agentic Reasoning Design Patterns in AI: Examples

In recent years, artificial intelligence (AI) has evolved to include more sophisticated and capable agents,…

2 months ago

LLMs for Adaptive Learning & Personalized Education

Adaptive learning helps in tailoring learning experiences to fit the unique needs of each student.…

2 months ago

Sparse Mixture of Experts (MoE) Models: Examples

With the increasing demand for more powerful machine learning (ML) systems that can handle diverse…

3 months ago

Anxiety Disorder Detection & Machine Learning Techniques

Anxiety is a common mental health condition that affects millions of people around the world.…

3 months ago

Confounder Features & Machine Learning Models: Examples

In machine learning, confounder features or variables can significantly affect the accuracy and validity of…

3 months ago

Credit Card Fraud Detection & Machine Learning

Last updated: 26 Sept, 2024 Credit card fraud detection is a major concern for credit…

3 months ago