What is Test Driven Development (TDD)? (Note that this is also sometimes referred to as Test Driven Design)
Test driven development (TDD) is composed of 3 steps during development of any code namely : write test, write code, refactor.
The below article gives a very good description of the same.
http://www.agiledata.org/essays/tdd.html
Mock Objects :
(from http://www.mockobjects.com/Faq.html)
What are Mock Objects ?
A mock object is used to test the behaviour of other objects. A mock object acts as a faux implementation of an interface or class that mimics the external behaviour of a true implementation. Second, a mock object observes how other objects interact with its methods and compares actual behaviour with preset expectations. When a discrepancy occurs, a mock object can interrupt the test and report the anomaly. If the discrepancy cannot be noted during the test, a verification method called by the tester ensures that all expectations have been met or failures reported.
What's an expectation?
When any component is used in a program, we anticipate a set of preconditions and postconditions. For example, we might anticipate that a database connection will be used once and then closed. We might also anticipate that a JDBC ResultSet will be passed a certain set of parameters for use with a given SQL command. A mock object can be told to expect conditions such as these, so that it can report back when our expectations are not met. The MockObjects API has refactored this mechanism into a framework of Expectation classes.
Why should I bother?
Today, most developers recognise the value of unit testing. But many developers also find that creating and maintaining non-trivial unit tests becomes difficult as products mature. Writing significant unit tests can tempt developers into making objects "test-aware". This can complicate production code and begins to defeat the purpose of unit testing. Mock objects simplify the creation and maintenance of non-trivial unit tests by allowing us to test one object at a time. Mock objects make it possible to test an object without its knowledge.
Mock objects allow you to unit test your application independently of the production infrastructure. Developers do not need access to a real database to run the unit tests. The same is true of any external resource, including hardware devices.
Mock objects can also simulate states that may be difficult or time-consuming to realize in a runtime environment. A mock object can throw any exception or produce any error condition on demand. For example, using a real database, to see how an object will react to an offline database you would have to actually down the database. Using a mock JDBC connection, you can simulate a dead database in one test and a live database in the next, all in a fraction of a second.
As products mature, and test suites grow larger, tracing the cause of a failed test can become difficult. A mock implementation can test assertions each time it interacts with production (or "domain") code. The test is then more likely to fail at the right time and generate a useful message.
We can also build base assertions about how domain objects are supposed to work into our mock objects. These assertions can be applied automatically whenever the mock object is used. This ensures that our base assertions remain true as the application matures.
We use mock objects because they allow us to quickly create inexpensive, effective tests that are easy to maintain.
Do I have to change the way I write my programs?
Usually not. In fact, mock objects should make it easier to test your programs without making changes to your production objects. Generally, the better designed your application, the easier it should be to test with mock objects. If an application does not test well, there are usually ways to improve its design and provide better testability. In practise, many developers find that approaching new development with a test-first perspective helps them write better programs more quickly. Writing tests for features helps define the objects we need to provide those features. Test-driven design helps us discover the interfaces our objects actually need.
What is the difference between stub objects and mock objects?
A stub object is the most minimal implementation of a class providing nothing more than the implementation of an interface. A mock object adds to this the ability to preload an object with data which can be played back during a test. Most importantly, it is also given details of the expected interactions that other objects will have with it and at the end of testing verify that these have taken place.
What Are JMocks :
JMocks help in creating the mock objects.
Before I get into the example, do glance through the below link which gives a good and simplified example of using JMocks for testing : http://jmock.codehaus.org/getting-started.html
Walkthrough of GextTextActionTest written for GetTextAction.java :
Consider the below class, GetTextAction.java : (Note:Only the required methods have been indicated in the below code)
class GetTextAction extends AbstractGetterAction { private OutputDock textDock; private TextAreaReactor reactor; public GetTextAction(TextAreaReactor reactor){ super(TextArea.GET_TEXT_ACTION, reactor.getIdentifier(), reactor.getActionLogger()); this.reactor = reactor; } public OutputDock getDefaultOutputDock() { return getTextDock(); } protected void fire() { getTextDock().setValue(reactor.getText().toString()); } protected void fillOutputDocks() { getOutputDocks().add(getNewTextDock()); } public OutputDockDictionary getOutputDocks(){ return outputDocks; } public TextAreaReactor getReactor() { return reactor; } public OutputDock getTextDock(){ return textDock; } public OutputDock getNewTextDock() { return new BasicOutputDock(String.class, new BasicDescriptor(TextArea.STRING_DOCK, "Contains the text from the TextArea")); } }
1)What do we want to test?
The key method as i could understand from the above class was 'fire()'. So i write a test for the 'fire()' method.
2)The actual test is as below :
package org.knownspace.fluency.swl.widget.reactor; import org.jmock.Mock; import org.jmock.cglib.MockObjectTestCase; import org.knownspace.fluency.persistence.ActionLogger; import org.knownspace.fluency.persistence.BasicActionLogger; import org.knownspace.fluency.common.action.BasicOutputDockDictionary; import org.knownspace.fluency.core.action.OutputDock; import org.knownspace.fluency.core.action.OutputDockDictionary; import org.knownspace.fluency.persistence.BasicIdentifier; public class GetTextActionTest extends MockObjectTestCase { private TextAreaReactor reactor; public void testFireAction(){ //setup mock output dock and instance Mock mockOutputDock = mock(OutputDock.class); OutputDock outputDock = (OutputDock) mockOutputDock.proxy(); //setup mock reactor and instance Mock mockReactor = mock(TextAreaReactor.class); reactor = (TextAreaReactor) mockReactor.proxy(); //setup mock logger and instance Mock mockLogger = mock(BasicActionLogger.class); ActionLogger logger = (ActionLogger) mockLogger.proxy(); //set expectations on the mock objects setExpectations(mockOutputDock, mockReactor, logger); //execute getTextAction(outputDock).fire(); //verify that expected methods were invoked verify(mockOutputDock, mockReactor, mockLogger); } private void verify(Mock mockOutputDock, Mock mockReactor, Mock mockLogger) { mockReactor.verify(); mockLogger.verify(); mockOutputDock.verify(); } private GetTextAction getTextAction(final OutputDock outputDock) { return new GetTextAction(reactor){ public OutputDock getTextDock() { return outputDock; } public OutputDockDictionary getOutputDocks(){ return new BasicOutputDictionaryStub(); } }; } private void setExpectations(Mock mockOutputDock, Mock mockReactor, ActionLogger logger) { mockReactor.expects(once()).method("getIdentifier").withNoArguments().will(returnValue(new BasicIdentifier())); mockReactor.expects(once()).method("getActionLogger").withNoArguments().will(returnValue(logger)); mockReactor.stubs().method("getText").withNoArguments().will(returnValue(new StringBuffer("Test string"))); mockOutputDock.stubs().method("setValue").with(eq("Test string")); } } class BasicOutputDictionaryStub extends BasicOutputDockDictionary{ public void add(OutputDock socket) { //this method can be used to set expectation counters //to check that the above method invocations caused this //method to be executed } }
3)The cgilib jmock package :
import org.jmock.cglib.MockObjectTestCase;
This supports mocking of concrete classes as against the non-cgilib versions that suppot only mocking of interfaces.
However a drawback of using this is that cgilib requires default contructors.
There are two solutions:
-add a default to all classes that needs to be mocked
-add interfaces for all classes that needs to be mocked.
Owing to the advantage obtained by using cglib, most of the developers prefer option 1(especially if their design is not suited to add interfaces) and the default constructor would be package level wherever possible with adequate checks in the build file to not use these default constructors anywhere other than in Tests.
4)Creating mock objects :
//setup mock output dock and instance Mock mockOutputDock = mock(OutputDock.class); OutputDock outputDock = (OutputDock) mockOutputDock.proxy(); //setup mock reactor and instance Mock mockReactor = mock(TextAreaReactor.class); reactor = (TextAreaReactor) mockReactor.proxy(); //setup mock logger and instance Mock mockLogger = mock(BasicActionLogger.class); ActionLogger logger = (ActionLogger) mockLogger.proxy();
So here, we create 3 pseudo-light weight instances of OutputDock, TextAreaReactor and ActionLogger.(the proxy() creates an instance).
How do we identify which classes to mock out?
-Examine the instance variables used by the class (TextAreaReactor, OutputDock)
-The constructor (above 2 and ActionLogger)
Basically all the classes required for the construction of the class, in this case constrution of GetTextAction.java requires the above 3 instances.
5)Setting expectations :
mockReactor.expects(once()).method("getIdentifier").withNoArguments().will(returnValue(new BasicIdentifier())); mockReactor.expects(once()).method("getActionLogger").withNoArguments().will(returnValue(logger)); mockReactor.stubs().method("getText").withNoArguments().will(returnValue(new StringBuffer("Test string"))); mockOutputDock.stubs().method("setValue").with(eq("Test string"));
In the 1st line, we say that , the method 'getIdentifier()' would be invoked once and this method would take in no arguments and would return and instance of a BasicIdentifier.
Likewise,the 2nd line indicates that the method 'getActionLogger()" would be invoked once with no arguments and would return an instance of logger.
The next 2 lines say that the method 'getText' and 'setValue' may be called but failure to call them would not fail the test.
Note :If 'getIdentifier' is written as 'GetIdentifier', the test would fail.
6)So far, we saw the setup that is required for the test.Now is the actual 'execution' of the test in the line :
getTextAction(outputDock).fire();
7)Finally, we need to verify to see if all the set expectations were met.
mockReactor.verify(); mockLogger.verify(); mockOutputDock.verify();
You could comment out one of these methods in the actual class to see the test fail on one of the 'verify' methods above.
8)What are Stubs :
"Stubs are typically used to stub out objects that are expensive to create or manipulate. A classic example might be a database connection. As a result you tend to most often find stubs at the edges of a system, or around complex clusters of objects within a system. To create a stub you form an alternative implementation of an interface and replace the real methods with simple canned data." -by Martin Fowler.
Example of a stub :
class BasicOutputDictionaryStub extends BasicOutputDockDictionary{ public void add(OutputDock socket) { //this method can be used to set expectation counters //to check that the above method invocations caused this //method to be executed } }
The above class is a Stub of BasicOutputActionDictionary.
We want to check that this method 'add' is being invoked as part of calling fire() on the action.
To test that i shall use ExpectationCounter (whichi shall implement and elaborate later) which is basically a flag which would be set to true inside the 'add' method.
Hence we can assert on that flag being true which implies that this method 'add' was executed.
9)JUnit vs Mock objects :
Many of you might have wondered as to why we need Mock objects when we have JUnit.When do we use one over the other?
JUnit itself only provides a framework against which to write simple tests. The concept is simple: For each Class you write, you also write a TestClass that contains all the tests for that class. The TestClass is a TestCase; that is, you must extend TestCase to create TestClass. It's possible to have multiple test cases for each class you write, but usually it's unnecessary. Test cases are gathered into test suites, and the whole thing runs at the press of a button.
JUnit has to be kept simple, so the creators haven't added the many features that developers have requested. Instead, they built the core framework so that it was easy to extend. Now developers choose just the libraries they need.
We can use JUnit to test simple classes with not many complex interactions or no heavy weight components/functionality like a database connection.
Mock Objects (JMocks help in creating the mock objects) :
Mock objects are extensions to JUnit.
Like i mentioned, they can be used to unit test when we have heavy weight objects like a database connection or even server side code like Servlets, Filters, Taglibs, EJBs, ...
The main goal of Mock objects is to unit test a method in isolation of other domain objects. Thus, the Mock Objetcs approach is to fake domain objects by using simulated copies instead of the real objects (the Mock Objects do not implement any logic in its fake objects, that would be "stubbing". All behaviours of mock objects are controlled by the unit test itself). This enables to finely unit test the method with no environment "noise" and to concentrate on unit testing its logic.
Note : The following articles makes very interesting reading where Martin Folwer gives a very colloquial definition and description of the following :
1)Refactoring : http://www.artima.com/intv/refactor.html
2)Design Principles and Code Ownership : http://www.artima.com/intv/principles.html
3)Upfront Design vs Evolutionary design : http://www.artima.com/intv/evolution.html
4)Flexibility and Complexity : http://www.artima.com/intv/flexplex.html
5)Test Driven Development : http://www.artima.com/intv/testdriven.html