EasyMock is a mock object framework for Java. You can find it here. The following example shows how to mock up the dependencies for your system under test or SUT. In this particular example, I'm testing a PricingServiceImpl class and it has a dependency to a DataAccess type. Using interface-based programming makes it easy to swap in different implementations of an interface type. I'm a firm believer in dependency injection (DI). Using DI makes mocking even easier when driving out a design with unit tests. First we have the interface types for PricingService and DataAccess:
1 package examples.easymock;
1 package examples.easymock;
Next up is the PricingServiceImpl class, which is the system under test or SUT for the subsequent unit test. Note that the PricingServiceImpl collaborates with a DataAccess type, but it does not take on the responsibility of creating the DataAccess implementation. That responsibility lies elsewhere in the application. In this scenarion, the unit test will provide the implementation (a mock implementation using EasyMock) and inject that instance into the PricingServiceImpl instance.
1 package examples.easymock; Finally the unit test. EasyMock is used to create mock instances of the DataAccess type, suitable for testing the PricingService implementation. 1 package examples.easymock; The setup of each test case is done in lines 23-27. The PricingService instance is created, a DataAccess mock implementation is factoried by EasyMock and the dependency between the PricingService and the DataAccess instance is fulfilled by injecting the mock DataAccess instance into the PricingService implementation. The SUT is ready for use. The first test case is a happy path test. We build up the expectation for the mocked dependency in line 34. We expect that the SUT (PricingServiceImpl) will call DataAccess.getPriceBySku(String sku), and for this test, we configure the mock to return a BigDecimal object, representing the price. Line 37 sets the mock into testing mode. The test is executed in line 39, invoking the PricingService.getPrice(String sku) method on the PricingService implementation. State and behavior verification occurs in lines 40 and 43. The second test case verifies that when the call to DataAccess returns null that our PricingService implementation will throw a SkuNotFoundException. Everything remains same except that the expectations of the DataAccess mock object changes (Line 51). It will return null when getPriceBySku(String sku) is invoked. Our service will be designed to test for null and throw a SkuNotFoundException when this situation occurs. The third test case verifies that our PricingServiceImpl will allow RuntimeExceptions to bubble out of it. RuntimeExceptions could be caused by a connection error to the database in DataAccess, for example. In any case, a RuntimeException signals a fatal error has occurred and that fail error should bubble up through the application and be handled appropriately at a higher level. PricingService will not take on the responsibility of handling RuntimeExceptions. Again, everything remains same except that the expectations of the DataAccess mock object changes (Line 64). It will throw a RuntimeException when getPriceBySku(String sku) is invoked. Our service will be designed to let this RuntimeException bubble out when this situation occurs. |