A. Unit testing is widely accepted as a "best practice" for software development. When you write an object, you must also provide an automated test class containing methods by calling its various public methods with various parameters and making sure that the values returned are appropriate.
When you're dealing with simple data or service objects, writing unit tests is straightforward. However, in reality the object under test rely on other objects or layers of infrastructure, and it is often expensive, impractical, or inefficient to instantiate these collaborators.
For example, to unit test an object that uses a database, it may be burdensome to install a local copy of the database, run your tests, then tear the local database down again. Mock objects provide a way out of this dilemma. A mock object conforms to the interface of the real object, but has just enough code to simulate the tested object and track its behavior. For example, a database connection for a particular unit test might record the query while always returning the same hard coded result. As long as the class being tested behaves as expected, it won't notice the difference, and the unit test can check that the proper query was emitted.
Here are some reasons why mock objects are handy:
- The unit tests as the name implies must test only a unit of the code and not all its collaborating dependencies. You only have to worry about the class under test. Mock objects allow you to achieve this by mocking external resource and coding dependencies. The example in the next question demonstrates how we can mock reading from a file, which is an external resource.
- The unit tests need to test for the proper boundary conditions. For example, positive values, negative values, zero value, etc. The mock object make your life easier for mimicking these boundary conditions.
- One of the biggest mistake one can make in writing quality unit tests is to have state dependencies between unit tests. The unit tests must be able to run in any order. The mock objects will help you isolate these state dependencies, and make your tests isolated and independent.For example
-- Test your service in isolation by mocking the calls to your DAO.
Having said this, too much mocking can make your code hard to read and understand. So, it is important to have the right balance without overdoing.
Q. How would you go about using mock objects in your unit tests?
A.
STEP 1:
Firstly, you need the relevant dependency jar files. A sample pom.xml file is shown below.
<properties>
<junit.version>4.8.1</junit.version>
<mockito.version>1.8.5</mockito.version>
<powermock.version>1.4.8</powermock.version>
</properties>
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
...
</dependencies>
<dependencyManagement>
STEP 2:
The UserDaoImpl is an implementation of the interface UserDao. The implementation read the user names from a text file users.tx.
The user.txt
Peter Smith
Aaron Lachlan
Zara John
Felix Chan
The UserDao interface
package unittest;
import java.util.List;
public interface UserDao {
public ListreadUsers() throws UsersException;
}
The UserDaoImpl class that reads from the user.txt file implements the interface UserDao
package unittest;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class UserDaoImpl implements UserDao {
private static final String DELIMITER = System.getProperty("line.separator");
public UserDaoImpl(){}
@Override
public ListreadUsers() throws UsersException {
InputStream is = getResource();
if (is == null) {
throw new UsersException("users file is not found");
}
Scanner sc = new Scanner(is);
String value = sc.useDelimiter(DELIMITER + "\r").next();
String[] users = value.split(DELIMITER);
return (users == null || users.length > 0 ? Arrays
.asList(users) : Collections.emptyList());
}
private InputStream getResource() {
ClassLoader cl = Thread.currentThread() .getContextClassLoader();
InputStream is = cl.getResourceAsStream("unittest/users.txt");
return is;
}
}
STEP 3:
Finally the unit test that uses the Mockito framework to mock the actual loading of the user names from text file. The user names will be supplied via the method getDummyIs(). The UserDaoImpl is partially mocked with the spy method. This means the getResource() methods is mocked by supplying some dummy data within the test itself. The readUsers() method is executed from the class under test, which is UserDaoImpl. The getResource() method is mocked to return a user name of "John Patrick" evey time it is invoked.
package unittest.test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import unittest.UserDao;
import unittest.UserDaoImpl;
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserDaoImpl.class)
public class UserDaoWithMockTest {
@Test
public void testGetUsers() throws Exception {
final UserDao partiallyMockedUserDao = PowerMockito
.spy(new UserDaoImpl());
PowerMockito.doReturn(getDummyIs()).when(
partiallyMockedUserDao, "getResource");
Listusers = partiallyMockedUserDao.readUsers();
Assert.assertEquals(1, users.size());
}
@Test(expected = unittest.UsersException.class)
public void testGetUsersNegative() throws Exception {
final UserDao partiallyMockedUserDao = PowerMockito
.spy(new UserDaoImpl());
PowerMockito.doReturn(null).when(partiallyMockedUserDao,
"getResource");
partiallyMockedUserDao.readUsers();
}
@Test(expected = unittest.UsersException.class)
public void testGetUsers2() throws Exception {
final UserDao partiallyMockedUserDao = PowerMockito
.spy(new UserDaoImpl());
PowerMockito
.doReturn(new ByteArrayInputStream("".getBytes()))
.when(partiallyMockedUserDao, "getResource");
Listusers = partiallyMockedUserDao.readUsers();
Assert.assertEquals(0, users.size());
}
public InputStream getDummyIs() {
String str = "John Patrick";
return new ByteArrayInputStream(str.getBytes());
}
}
Q. What mocking frameworks have you used?
A. Mockito, EasyMock, and PowerMock.
PowerMock is a framework that extends other mock libraries such as EasyMock and Mockito with more powerful capabilities like mocking of static methods, constructors, final classes and methods, private methods, removal of static initializers and more.