Over a million developers have joined DZone.

Test-Driven Development (TDD) Shines with Mocking

· Agile Zone

Learn more about how DevOps teams must adopt a more agile development process, working in parallel instead of waiting on other teams to finish their components or for resources to become available, brought to you in partnership with CA Technologies.

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

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.tdd_thumb
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.

Discover the warning signs of DevOps Dysfunction and learn how to get back on the right track, brought to you in partnership with CA Technologies.

Topics:
java ,opinion ,devops ,testing ,unit testing ,tdd ,test-driven development ,mocking ,testing attributes

Published at DZone with permission of Ajitesh Kumar, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}