1. Unit Testing & test doubles
1.1. Target & challenge of unit testing
A unit test is a test related to a single responsibility of a
single class, often referred to as the System Under Test (SUT). A unit test should test a class in isolation. Side effects from other classes or the system should be eliminated if possible. The achievement of this desired goal is typical complicated by the fact that Java classes usually depend on other classes. Test doubles will solve this.
The purpose of unit tests is to verify that the code in an SUT works. A tested object usually talks to other objects known as collaborators. These collaborators need to be created so the tested object can be assigned to them in the test. To make unit testing simpler and allow control of all aspects of the execution context, it is useful to replace the real cooperating objects with their fake replacements called test doubles. They look like the originals, but do not have any dependencies to other objects. Test doubles can also be easily programmed with specific expectations, such as recording any interactions they've had.
The purpose of unit tests is to verify that the code in an SUT works. A tested object usually talks to other objects known as collaborators. These collaborators need to be created so the tested object can be assigned to them in the test. To make unit testing simpler and allow control of all aspects of the execution context, it is useful to replace the real cooperating objects with their fake replacements called test doubles. They look like the originals, but do not have any dependencies to other objects. Test doubles can also be easily programmed with specific expectations, such as recording any interactions they've had.
To make it clearer, try to imagine code for a typical enterprise system. Now here's a service with some logic that needs two classes to fulfill its responsibility. Both classes then require a few other classes. One of these other classes could be a DAO, which needs access to a database, while yet another requires a message queue. It would be quite an effort to create that hierarchy and provide required resources. There could also be problems while running that kind of test, e.g., long startup times or the inability to test multiple developer stations simultaneously. Using Mocks, though, the same test could be much cleaner and faster.
Test doubles can be divided into a few groups :
- Dummy - an empty object passed in an invocation (usually only to satisfy a compiler when a method argument is required). A dummy object is passed around but never used, i.e., its methods are never called. Such an object can for example be used to fill the parameter list of a method
- Fake - an object having a functional/working implementation, but usually in a simplified form, just to satisfy the test (e.g., an in-memory database and not a real database)
- Stub - an object with hardcoded behavior suitable for a given test (or a group of tests). A stub class is an partial implementation for an interface or class with the purpose of using an instance of this stub class during testing. Stubs usually do responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls. Stub: A class or object that implements the methods of the class/object to be faked and returns always what you want. Stub helps us to run test. How? It gives values which helps to run test. These values are itself not real and we created these values just to run the test. For example we create a HashMap to give us values which are similar to values in database table. So instead of directly interacting with database we interact with Hashmap.
- Mock - an object with the ability to a) have a programmed expected
behavior, and b) verify the interactions occurring in its lifetime (this
object is usually created with the help of mocking framework). A mock object is a dummy implementation for an interface or a class in which you define the output of certain method calls. Mock: The same of stub, but it adds some logic that "verifies" when a method is called so you can be sure some implementation is calling that method. Mock is an fake object which runs the test where we put assert. A Mock is like a stub, only it also has methods that make it possible determine what methods where called on the Mock. Mocks vs Stubs = Behavioral testing vs State testing. According to the principle of Test only one thing per test, there may be several stubs in one test, but generally there is only one mock. Test lifecycle with stubs:
- Setup - Prepare object that is being tested and its stubs collaborators.
- Exercise - Test the functionality.
- Verify state - Use asserts to check object's state.
- Teardown - Clean up resources.
Test lifecycle with mocks:- Setup data - Prepare object that is being tested.
- Setup expectations - Prepare expectations in mock that is being used by primary object.
- Exercise - Test the functionality.
- Verify expectations - Verify that correct methods has been invoked in mock.
- Verify state - Use asserts to check object's state.
- Teardown - Clean up resources.
- Spy - a mock created as a proxy to an existing real object; some methods can be stubbed, while the un- stubbed ones are forwarded to the covered object
Test doubles can be passed to other objects which are tested. Your tests can validate that the class reacts correctly during tests, i.e., you can validate if certain methods on the mock object were called. This helps to ensure that you only test the class while running tests and that your tests are not affected by any side effects.
Mock objects are typically configured. Mock objects typically require less code to configure and should therefore be preferred.
For a detailed discussion on this topic, see Mocks Aren't Stubs from Martin Fowler.
1.2. Mock object generation
You can create these mock objects manually (via code) or use a mock framework to simulate these classes. Mock frameworks allow you to create mock objects at runtime and define their behavior.
The classical example for a mock object is a data provider. In production a real database is used, but for testing a mock object simulates the database and ensures that the test conditions are always the same.
These mock objects can be provided to the class which is tested. Therefore, the class to be tested should avoid any hard dependency on external data.
Mocking or mock frameworks allows testing the expected interaction with the mock object, e.g., you test which methods have been called on the mock object.
2. Mocking frameworks
Mocking frameworks make the creation of mock objects as simple as possible. Popular mock frameworks are EasyMock, jMock and Mockito.
The following lists the links to these frameworks.
# jMock http://jmock.org/ # EasyMock http://easymock.org/ # Mockito https://github.com/mockito/mockito
Mockito is a popular mock framework which can be used in conjunction with JUnit. Mockito allows you to create and configure mock objects. Using Mockito simplifies the development of tests for classes with external dependencies significantly.
4. Adding Mockito as dependencies to a project
If you use Gradle as your build system, add the following dependency to your build file (build.gradle).
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-all:1.10.19" }
Add following configuration into pom.xm filel:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.10.19</version> </dependency> </dependencies>
4.3. Using Eclipse
The same as Using Maven scenarios above, but I can create 'simple maven project' from eclipse and add dependencies into pom.xml file.
4.3. Using Grade & Eclipse
build.gradle:
apply plugin: 'eclipse'
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-all:1.10.19" }
5. Using the Mockito API
Mockito supports the creation of mock objects with the static
mock()
. If you add a static import fororg.mockito.Mockito.*;
, you can access Mockitos methods like mock()
directly. Static imports allows you to call static members, i.e., methods and fields of a class directly without specifying the class.
To configure which values are returned at a method call Mockito defines a fluent API. You can use the
verify()
method to ensure that a method was called.
The
when(....).thenReturn(....)
call chain can be used to specify a condition and a return value for this condition. If you specify more than one value, they are returned in the order of specification until the last one is used. Afterwards the last specified value is returned. Mocks can also return different values depending on arguments passed into a method. You also use methods like anyString
or anyInt
to define that independent of the input value a certain return value should be returned.import static org.mockito.Mockito.*; import static org.junit.Assert.*; @Test public void test1() { // create mock MyClass test = Mockito.mock(MyClass.class); // define return value for method getUniqueId() when(test.getUniqueId()).thenReturn(43); // use mock in test.... assertEquals(test.getUniqueId(), 43); } // Demonstrates the return of multiple values @Test public void testMoreThanOneReturnValue() { Iterator i= mock(Iterator.class); when(i.next()).thenReturn("Mockito").thenReturn("rocks"); String result=i.next()+" "+i.next(); //assert assertEquals("Mockito rocks", result); } // this test demonstrates how to return values based on the input @Test public void testReturnValueDependentOnMethodParameter() { Comparable c= mock(Comparable.class); when(c.compareTo("Mockito")).thenReturn(1); when(c.compareTo("Eclipse")).thenReturn(2); //assert assertEquals(1,c.compareTo("Mockito")); } // this test demonstrates how to return values independent of the input value @Test public void testReturnValueInDependentOnMethodParameter() { Comparable c= mock(Comparable.class); when(c.compareTo(anyInt())).thenReturn(-1); //assert assertEquals(-1 ,c.compareTo(9)); } // return a value based on the type of the provide parameter @Test public void testReturnValueInDependentOnMethodParameter() { Comparable c= mock(Comparable.class); when(c.compareTo(isA(Todo.class))).thenReturn(0); //assert Todo todo = new Todo(5); assertEquals(todo ,c.compareTo(new Todo(1))); }
The
doReturn(...).when(...).methodCall
call chain works similar but is useful for void methods. ThedoThrow
variant can be used for methods which return void
to through for example an exception. This usage is demonstrated by the following code snippet.import static org.mockito.Mockito.*; import static org.junit.Assert.*; // this test demonstrates how use doThrow @Test(expected=IOException.class) public void testForIOException() { // create an configure mock OutputStream mockStream = mock(OutputStream.class); doThrow(new IOException()).when(mockStream).close(); // use mock OutputStreamWriter streamWriter= new OutputStreamWriter(mockStream); streamWriter.close(); }
Mockito keeps track of all the method calls and their parameters to the mock object. You can use the
verify()
method on the mock object to verify that the specified conditions are met, i.e., that a method has been called with certain parameters. This kind of testing is sometimes called behavior testing, because it does not check the result of a method call, but it checks that a method is called with the right parameters.import static org.mockito.Mockito.*; @Test public void testVerify() { // create and configure mock MyClass test = Mockito.mock(MyClass.class); when(test.getUniqueId()).thenReturn(43); // call method testing on the mock with parameter 12 test.testing(12); test.getUniqueId(); test.getUniqueId(); // now check if method testing was called with the parameter 12 verify(test).testing(Matchers.eq(12)); // was the method called twice? verify(test, times(2)).getUniqueId(); // other alternatives for verifiying the number of method calls for // a method verify(mock, never()).someMethod("never called"); verify(mock, atLeastOnce()).someMethod("called at least once"); verify(mock, atLeast(2)).someMethod("called at least twice"); verify(mock, times(5)).someMethod("called five times"); verify(mock, atMost(3)).someMethod("called at most 3 times"); }
@Spy or the
spy()
method can be used to wrap a real object. Every call, unless specified otherwise, is delegated to the object.import static org.mockito.Mockito.*; // Lets mock a LinkedList List list = new LinkedList(); List spy = spy(list); //You have to use doReturn() for stubbing doReturn("foo").when(spy).get(0); // this would not work // real method is called so spy.get(0) // throws IndexOutOfBoundsException (list is still empty) when(spy.get(0)).thenReturn("foo");
The
verifyNoMoreInteractions()
allows you to check that no other method was called.
You also have the
@InjectMocks
annotation which tries to do constructor, method or field dependency injection based on the type. The following code is a slightly modified example from the Javadoc.// Mockito can construct this class via constructor public class ArticleManager { ArticleManager(ArticleCalculator calculator, ArticleDatabase database) { } } // Mockito can also perform method injection public class ArticleManager { ArticleManager() { } void setDatabase(ArticleDatabase database) { } void setCalculator(ArticleCalculator calculator) { } } // Mockito can also perform field injection public class ArticleManager { private ArticleDatabase database; private ArticleCalculator calculator; }
@RunWith(MockitoJUnitRunner.class) public class ArticleManagerTest { @Mock private ArticleCalculator calculator; @Mock private ArticleDatabase database; @Spy private UserProvider userProvider = new ConsumerUserProvider(); // creates instance of ArticleManager // and performs constructor injection on it @InjectMocks private ArticleManager manager; @Test public void shouldDoSomething() { // assume that ArticleManager has a method called initialize // which calls a method // addListener with an instance of ArticleListener manager.initialize(); // validate that addListener was called verify(database).addListener(any(ArticleListener.class)); } }
For more information see the Javadoc of InjectMock.
Mockito also supports the creation of mock objects based on the
@Mock
annotation. If you use annotations, you must initialize this mock objects with a MockitoAnnotations.initMocks(this)
method call or annotate your class with the @RunWith(MockitoJUnitRunner.class)
annotation to use the Mockito test runner.import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class MockitoTest { // assume there is a class MyDatabase @Mock MyDatabase databaseMock; @Test public void testQuery() { // assume there is a class called ClassToTest // which could be tested ClassToTest t = new ClassToTest(databaseMock); // call a method boolean check = t.query("* from t"); // test the return type assertTrue(check); // test that the query() method on the // mock object was called verify(databaseMock).query("* from t"); } }
Mockito has certain limitations. It can not test the following constructs:
- final classes
- anonymous classes
- primitive types
6. Using Mockito on Android
Mockito can also be directly used in Android unit tests simply by adding the dependency to Mockito to the Gradle build file of the application. To use it in instrumented Android tests (since the release 1.9.5). Which requires that dexmaker and dexmaker-mockito are also added as dependency. in the Gradle build file.
dependencies { testCompile 'junit:junit:4.12' // required if you want to use Mockito for unit tests testCompile 'org.mockito:mockito-core:1.+' // required if you want to use Mockito for Android instrumentation tests androidTestCompile 'org.mockito:mockito-core:1.+' androidTestCompile "com.google.dexmaker:dexmaker:1.2" androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2" }
Create an Android application with the package name
com.vogella.android.testing.mockito.contextmock
. Add a static method which allows to create an intent with certain parameters as in the following example.public static Intent createQuery(Context context, String query, String value) { // Reuse MainActivity for simplification Intent i = new Intent(context, MainActivity.class); i.putExtra("QUERY", query); i.putExtra("VALUE", value); return i; }
dependencies { // the following is required to use Mockito and JUnit for your // instrumentation unit tests on the JVM androidTestCompile 'junit:junit:4.12' androidTestCompile 'org.mockito:mockito-core:1.+' androidTestCompile 'com.android.support.test:runner:0.3' androidTestCompile "com.google.dexmaker:dexmaker:1.2" androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2" // the following is required to use Mockito and JUnit for your unit // tests on the JVM testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.+' }
Create a new unit test using Mockito to check that the intent is triggered with the correct extra data.
For this you mock the
Context
object with Mockito as in the following example.package com.vogella.android.testing.mockitocontextmock; import android.content.Context; import android.content.Intent; import android.os.Bundle; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class TextIntentCreation { @Test public void testIntentShouldBeCreated() { Context context = Mockito.mock(Context.class); Intent intent = MainActivity.createQuery(context, "query", "value"); assertNotNull(intent); Bundle extras = intent.getExtras(); assertNotNull(extras); assertEquals("query", extras.getString("QUERY")); assertEquals("value", extras.getString("VALUE")); } }
Creating Mock
A mock can be created with the help of a static method mock():
Flower flowerMock=Mockito.mock(Flower.class);
But there's another option: use of @Mock annotation:
private Flower flowerMock;
Warning:
If you want to use @Mock or any other Mockito annotations, it is
required to call MockitoAnnotations.initMocks( testClass ) or use
MockitoJUnit4Runner as a JUnit runner (see the annotation section below
for more information).
Stubbing Method's Returned Value
One of the basic functions of mocking
frameworks is an ability to return a given value when a specific method
is called. It can be done using Mockito.when() in conjunction with
thenReturn () . This process of defining how a given mock method should
behave is called stubbing.
Warning: Note
that the examples in this Refcard were created to demonstrate behaviors
of Mockito in a specific context. Of course, when writing the test for
your codebase, there is no need to ensure that mocks are stubbed
correctly.
HotTip: Non-void methods return by default an "empty" value appropriate for its type (e.g., null, 0, false, empty collection).
Following an arrange-act-assert pattern (similar to given- when-then, from Behavior Driven Development) a test should be split into three parts (blocks), each with a specified responsibility.
This way, what is being tested, is clearly separated from the setup and verification parts. To integrate cleanly with Behavior Driven Development semantics Mockito contains BDDMockito class which introduces an alias given() which can be used instead of when() method while stubbing. Here's the previous example, but now using the BDD semantics:
given-when-then comments make intentions of tests clearer.
HotTip: Mockito makes heavy use of static methods. It is good to use static imports to make code shorter and more readable. IDE can be used to automatize adding
static imports.
static imports.
Mockito provides a family of functions for requesting specific behaviors ring.
HotTip: Non-void methods return by default an "empty" value appropriate for its type (e.g., null, 0, false, empty collection).
Following an arrange-act-assert pattern (similar to given- when-then, from Behavior Driven Development) a test should be split into three parts (blocks), each with a specified responsibility.
This way, what is being tested, is clearly separated from the setup and verification parts. To integrate cleanly with Behavior Driven Development semantics Mockito contains BDDMockito class which introduces an alias given() which can be used instead of when() method while stubbing. Here's the previous example, but now using the BDD semantics:
given-when-then comments make intentions of tests clearer.
Section 5: Argument Matching
Section 6: Stubbing Multiple Calls to the Same Method
Section 7: Stubbing Void Methods
Section 8: Stubbing With a Custom Answer
Section 9: Verifying Behavior
Section 10: Verifying Call Order
Section 11: Verifying With Argument Matching
Section 12: Verifying With Timeout
Section 13: Spying on Real Objects
Section 14: Annotations
Section 15: Changing the Mock Default Return Value
Section 16: Resetting Mock
Section 17: Limitations
0 comments:
Post a Comment