JUnit and EasyMock cooperation
Developers always need to take care about code which they produced. They should be ensured that code works properly after a new feature was implemented or some bug was fixed. That’s can be achieved at least with the help of unit tests. Since this blog is dedicated to Java programming language, today I will write the article about JUnit 4.1 and EasyMock 3.1 frameworks. The main purpose of these frameworks is to make writing of unit tests easier.
Introduction
For the demonstration of JUnit and EasyMock capabilities I need to have some code which I’m going to cover with tests. So at the start I’m going to introduce you a simple
application which I have developed for this tutorial. It should be very familiar for Java developers, because it emulates work of a coffee machine. Now I will describe in a few words functionality of the application. Coffee machine has two containers: for the water and for the coffee. You can make three varieties of coffee depends on portion size (small, medium, large).
- Positive scenario: you make a portion of coffee and containers have enough water and coffee beans.
- Negative scenario: you make a portion of coffee and containers haven’t enough water or coffee beans.
If any container isn’t complete enough you can refill it. After the concise description of application’s functionality you can easily imagine how it should work. It’s time to provide code samples of classes.
Application code
Now try to be more patient and attentive. You will see a plenty of classes below, which I’m going to test in the next paragraph. As was mentioned above, coffee machine can produce three types of coffee depends on portion size you want to get. Portions in the application will be represented as an enumeration.
public enum Portion { SMALL(1), MEDIUM(2), LARGE(3); private int size; private Portion(int size) { this.size = size; } public int size() { return size; } }
Our coffee machine has two containers, that’s mean we need to build logical architecture for them. Let’s adhere to smart coding approaches based on interfaces.
public interface IContainer { public boolean getPortion(Portion portion) throws NotEnoughException; public int getCurrentVolume(); public int getTotalVolume(); public void refillContainer(); }
In order to avoid a code dublication I need to develop an abstract class for the container. In context of this programming approach I want to bethink one of my posts about Abstract class VS Interface.
public abstract class AbstractContainer implements IContainer { private int containerTotalVolume; private int currentVolume; public AbstractContainer(int volume) { if (volume < 1) throw new IllegalArgumentException('Container's value must be greater then 0.'); containerTotalVolume = volume; currentVolume = volume; } @Override public boolean getPortion(Portion portion) throws NotEnoughException { int delta = currentVolume - portion.size(); if (delta > -1) { currentVolume -= portion.size(); return true; } else throw new NotEnoughException('Refill the ' + this.getClass().getName()); } @Override public int getCurrentVolume() { return currentVolume; } @Override public int getTotalVolume() { return containerTotalVolume; } @Override public void refillContainer() { currentVolume = containerTotalVolume; } }
Methods in the Abstract Container are self explained, so there is no need to stop on them more detail. You probably noticed NotEnoughException, don’t worry, it’s nothing special, just custom specific exception for the application.
public class NotEnoughException extends Exception { public NotEnoughException(String text) { super(text); } }
After development of container interface and abstract class were completed, we can proceed with concrete container realisation.
public class CoffeeContainer extends AbstractContainer { public CoffeeContainer(int volume) { super(volume); } }
The same class will be for the water container:
public class WaterContainer extends AbstractContainer { public WaterContainer(int volume) { super(volume); } }
Now we have all required stuff to develop code related to the coffee machine. As previously, I will start from interface development.
public interface ICoffeeMachine { public boolean makeCoffee(Portion portion) throws NotEnoughException; public IContainer getCoffeeContainer(); public IContainer getWaterContainer(); }
And finally here is a realisation of coffee machine:
public class CoffeeMachine implements ICoffeeMachine { private IContainer coffeeContainer; private IContainer waterContainer; public CoffeeMachine(IContainer cContainer, IContainer wContainer) { coffeeContainer = cContainer; waterContainer = wContainer; } @Override public boolean makeCoffee(Portion portion) throws NotEnoughException { boolean isEnoughCoffee = coffeeContainer.getPortion(portion); boolean isEnoughWater = waterContainer.getPortion(portion); if (isEnoughCoffee && isEnoughWater) { return true; } else { return false; } } @Override public IContainer getWaterContainer() { return waterContainer; } @Override public IContainer getCoffeeContainer() { return coffeeContainer; } }
That’s it, what relates to the program which I’m going to test with the help of unit tests.
JUnit testing
Before I start a JUnit tests development, I want to repeat canonical aims of unit tests. An unit test checks the smallest part of functionality – method or a class. This circumstance imposes some logical restrictions on a development. That’s mean that you don’t need to put some extra logic in a method, because after this it becomes more difficult for a testing. And one more important thing – unit testing implies an isolation of functionality from other parts of application. We don’t need to check functionality of a method “A” while we are working with a method “B”. So let’s stat to write JUnit tests for the coffee machine application. For this purpose we need to add some dependencies to the pom.xml
... <dependency> <groupid>org.easymock</groupid> <artifactid>easymock</artifactid> <version>3.1</version> </dependency> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>4.11</version> </dependency> ...
I have choosen AbstractContainer class for the demonstration of JUnit tests. Because in context of the application we have two realisations of this class and if we will write tests for it, automaticaly we will test WaterContainer class and CoffeeContainer class.
import static org.junit.Assert.assertEquals; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.app.data.Portion; import com.app.exceptions.NotEnoughException; import com.app.mechanism.WaterContainer; import com.app.mechanism.interfaces.IContainer; public class AbstractContainerTest { IContainer waterContainer; private final static int VOLUME = 10; @Before public void beforeTest() { waterContainer = new WaterContainer(VOLUME); } @After public void afterTest() { waterContainer = null; } @Test(expected = IllegalArgumentException.class) public void testAbstractContainer() { waterContainer = new WaterContainer(0); } @Test public void testGetPortion() throws NotEnoughException { int expCurVolume = VOLUME; waterContainer.getPortion(Portion.SMALL); expCurVolume -= Portion.SMALL.size(); assertEquals('Calculation for the SMALL portion is incorrect', expCurVolume, waterContainer.getCurrentVolume()); waterContainer.getPortion(Portion.MEDIUM); expCurVolume -= Portion.MEDIUM.size(); assertEquals('Calculation for the MEDIUM portion is incorrect', expCurVolume, waterContainer.getCurrentVolume()); waterContainer.getPortion(Portion.LARGE); expCurVolume -= Portion.LARGE.size(); assertEquals('Calculation for the LARGE portion is incorrect', expCurVolume, waterContainer.getCurrentVolume()); } @Test(expected = NotEnoughException.class) public void testNotEnoughException() throws NotEnoughException { waterContainer.getPortion(Portion.LARGE); waterContainer.getPortion(Portion.LARGE); waterContainer.getPortion(Portion.LARGE); waterContainer.getPortion(Portion.LARGE); } @Test public void testGetCurrentVolume() { assertEquals('Current volume has incorrect value.', VOLUME, waterContainer.getCurrentVolume()); } @Test public void testGetTotalVolume() { assertEquals('Total volume has incorrect value.', VOLUME, waterContainer.getTotalVolume()); } @Test public void testRefillContainer() throws NotEnoughException { waterContainer.getPortion(Portion.SMALL); waterContainer.refillContainer(); assertEquals('Refill functionality works incorectly.', VOLUME, waterContainer.getCurrentVolume()); } }
I need to explain, for what all of annotations are used. But I’m to lazy for this and I just give you a link to the JUnit API. There you can read the most correct explanations. Notice common things for all tests – they are all marked with @Test annotation, it indicates that a following method is a test, and every test ends with some of “assert” methods. Assertions are an essential part for each test, because all manipulations in a test should be checked in the end of it.
JUnit with EasyMock testing
Ok, in the previous paragraph I show you the example of sveral simple JUnit tests. In that example tests doesn’t colaborate with any other classes. What if we need to involve some extra class in a JUnit test? I mentioned above that unit tests should be isolated from a rest application’s functionality. For this purpose you can use EasyMock testing framework. With the help of EasyMock you can crate mocks. Mocks are objects which emulates behaviour of real concrete object, but with the one big plus, you can specify state for the mock, and in this way you obtain that state for the fake object which you need in particular moment of unit test.
import static org.junit.Assert.*; import org.easymock.EasyMock; import org.junit.After; import org.junit.Before; import org.junit.Test; public class CoffeeMachineTest { ICoffeeMachine coffeeMachine; IContainer coffeeContainer; IContainer waterContainer; @Before public void setUp() { coffeeContainer = EasyMock.createMock(CoffeeContainer.class); waterContainer = EasyMock.createMock(WaterContainer.class); coffeeMachine = new CoffeeMachine(coffeeContainer, waterContainer); } @After public void tearDown() { coffeeContainer = null; waterContainer = null; coffeeMachine = null; } @Test public void testMakeCoffe() throws NotEnoughException { EasyMock.expect(coffeeContainer.getPortion(Portion.LARGE)).andReturn(true); EasyMock.replay(coffeeContainer); EasyMock.expect(waterContainer.getPortion(Portion.LARGE)).andReturn(true); EasyMock.replay(waterContainer); assertTrue(coffeeMachine.makeCoffee(Portion.LARGE)); } @Test public void testNotEnoughException() throws NotEnoughException { EasyMock.expect(coffeeContainer.getPortion(Portion.LARGE)).andReturn(false); EasyMock.replay(coffeeContainer); EasyMock.expect(waterContainer.getPortion(Portion.LARGE)).andReturn(true); EasyMock.replay(waterContainer); assertFalse(coffeeMachine.makeCoffee(Portion.LARGE)); } }
In the previous code snippet you see cooperation of JUnit and EasyMock. I can underline several fundamental things in EasyMock usage.
- If test requires interaction with some external object you should mock it.
coffeeContainer = EasyMock.createMock(CoffeeContainer.class);
- Set a behavior for a mock or for a concrete method which is required for the testing of object under the test.
EasyMock.expect(coffeeContainer.getPortion(Portion.LARGE)).andReturn(true);
- Switch a mock to a reply mode.
EasyMock.replay(coffeeContainer);
EasyMock has a lot of different methods its API not small, so I recomend to read more on the official site.
JUnit test suite
When you have a small application, you can launch JUnit tests separately, but what if you work on a large and complex application? In this case unit tests can be aggregated in test suits by some feature. JUnit provide convenient way for this.
@RunWith(Suite.class) @SuiteClasses({ AbstractContainerTest.class, CoffeeMachineTest.class }) public class AllTests { }
Summary
Unit testing it’s a very important part of software development, it has a lot of approaches, methodologies and tools. In this post I have made an overview of JUnit and EasyMock, but I omited many interesting moments and technics which I plan to cover in following tutorials. You can download source code of the tutorial from my DropBox.
Reference: JUnit and EasyMock cooperation from our JCG partner Alex Fruzenshtein at the Fruzenshtein’s notes blog.