- Butterfly DI Container
- Getting Started With Butterfly DI Container
- Butterfly Container Script
- Extending Butterfly Container Script With Constants
- Extending Butterfly Container Script With Functions
- Extending Butterfly Container Script With Instantiation Modes
- Injecting ThreadLocal Objects
- Localization and Internationalization
- Replacing Factories
- Mock Dependencies
- Unit Testing
- Butterfly Container vs. Others
- Butterfly Container Performance
- Butterfly Container Script Type Safety
- Why Not XML?
Sometimes you may want to test a component using a mock dependency rather than its real dependency. This is easily done by simply replacing the factory of the real dependency with a mock factory. As illustrated in "Unit Testing Components and Factories" you can replace the factory inside the unit test.
Butterfly Testing Tools
Rather than implementing mock objects by hand you can use the Butterfly Testing Tools to create dynamic mock objects at test-runtime. Here is a factory configuration example:
myService = com.myapp.MyService(); myComponent = com.myapp.MyComponent(myService);
Using Butterfly Testing Tools it is possible to create mock implementations of an interface. Mock implementations are also called mock objects. A mock object records what methods were called on it, so you can make assertions about method call history. The return values of the methods called can also be freely configured, so you can test that the component using the mock object behaves correctly in case of different return values.
Assuming that the MyService class implements an interface called IService, you can create a mock implementation of the IService interface using this code:
Imagine that the MyService class implements an interface called IService. Here is how you can create a mock implementation of that service.
mockService = * com.jenkov.testing.mock.impl.MockFactory.createProxy( (java.lang.Class) com.jenkov.container.IService.class );
This mockService factory returns a dynamic implementation of the IService interface based on Java's java.lang.reflect.Proxy class. The proxy returned will return null from all method calls on it, and do nothing else, by default.
In order to make the mockService return mock values from method calls on it, or check what methods were called on it, you need to obtain the IMock instance associated with the IService mock object. Here is how that is done:
IMock mock = MockFactory.getMock(serviceMock);
While you can configure the container to do this, it is actually easier to just write this code directly in your unit test. The serviceMock instance passed to the getMock() method should be the mock object obtained from the mockService factory, using the container.instance("mockService") method call. In other words, the instance returned from the MockFactory.createProxy() method call.
The IMock instance has a few useful methods:
addReturnValue(...) getInvocations() assertInvoked(...) invoked(...)
You can read more about these methods on the Butterfly Testing Tools project page.
Just like it is possible to create mock implementations of an interface, it is possible to create proxies for real collaborators, as long as these collaborators implement one or more interfaces. For instance, it is possible to wrap a database connection in a proxy. The proxy records all method calls on it, and then forwards the method calls to the real database connection. This way it is possible to detect if close() is called on the database connection after some method call that uses the connection has completed.
Here is how a collaborator is wrapped in a proxy:
Connection con = (Connection) MockFactory.createProxy(databaseConnection);
All calls on the con connection proxy are now recorded and forwarded to the databaseConnection instance. The object returned by databaseConnection is returned by the con proxy too.
You can now obtain the IMock instance associated with the proxy like this:
IMock mock = MockFactory.getMock(con);
From the IMock instance you can see what methods were invoked on the con connection proxy.
If you have the following configuration for production in your application
dataSource = 1 com.myapp.MyDataSource( "oracleDriverClass", "oracleUrl", "user", "password"); personDao = * com.myapp.PersonDao(dataSource.getConnection());
You could create an extra configuration of the personDao for test:
colProxy = * com.jenkov.testing.mock.impl.MockFactory .createProxy((java.lang.Object) $0); personDaoTest = * com.myapp.PersonDao(colProxy(dataSource.getConnection()));
These two factory configurations could be added to the container from inside your unit test. They do not have to be part of the application container configuration script.
In order to access the IMock associated with the connection proxy you need access to the proxy connection. And then you need to run this code:
IMock mock = MockFactory.getMock(connectionProxy);
Exactly how you obtain the database connection proxy from the PersonDao is up to you to decide. A suggestion would be to have a PersonDao.getConnection() method, even though you'd probably mostly use it during testing.