coffee machine java
Developers always need to take care about code they produced. They should be ensured that the code works properly after a new feature was implemented or some bug was fixed. That can be achieved with the help of unit tests. Since this blog is dedicated to the Java programming language, today I will write an article about the JUnit 4.1 and EasyMock 3.1 frameworks. The main purpose of these frameworks is to make writing of unit tests easier. As alternative you can read about JUnit and Mockito.

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 present 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. As you probably know, Coffee machine has two containers: one to hold coffee and the other for water. Basically, coffee is available in three sizes: small, medium, and 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 the 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 plenty of classes below, which I’m going to test in the next paragraph.
As mentioned above, coffee machine can produce three sizes of coffee depending on portion 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 means 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 code duplication I need to develop an abstract class for the container. In context of this programming approach I want to bethink of 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 in detail. You have probably noticed NotEnoughException, don’t worry, there is nothing special, just custom specific exception for the application.

public class NotEnoughException extends Exception {
	
	public NotEnoughException(String text) {
		super(text);
	}

}

After the container interface is developed and the abstract class is completed, we can proceed with a concrete container realization.

public class CoffeeContainer extends AbstractContainer {

	public CoffeeContainer(int volume) {
		super(volume);
	}

}

The same class will be used 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 done, 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 the realization of the 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;
	}

}

This is all that is needed for the program which I’m going to test with the help of unit tests.

JUnit testing

Before I start JUnit tests development, I want to repeat canonical aims of unit tests. A unit test checks the smallest part of functionality – a method or a class. This circumstance imposes some logical restrictions on development. That means that you don’t need to put some extra logic in a method, because after this it becomes more difficult for testing. And one more important thing – unit testing implies isolation of functionality from other parts of the application. We don’t need to check functionality of a method “A” while we are working with a method “B”.

So let’s start to write JUnit tests for the coffee machine application. For this purpose we need to add some dependencies to the pom.xml file

...
		<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 chosen the AbstractContainer class for the demonstration of JUnit tests, because in context of the application we have two realizations of this class. If we write tests for it, we will automaticaly test the WaterContainer class and the 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 purpose all the annotations are used. But I’m too lazy to do this, and I just give you a link to the JUnit API. There you can read the most correct explanations.
Pay attention to common things for all tests – they are all marked with @Test annotation, it indicates that the following method is a test, and every test ends with some of the “assert” methods. Assertions are an essential part of 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 showed you the example of several simple JUnit tests. In that example tests don’t interact 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 the rest of the application’s functionality. For this purpose you can use the EasyMock testing framework.
With the help of EasyMock you can crate mocks. Mocks are objects which emulate behaviour a of real concrete object, but with 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 at a 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 interaction of JUnit and EasyMock. I can underline several fundamental things in EasyMock usage.
1. If test requires interaction with some external object you should mock it.

coffeeContainer = EasyMock.createMock(CoffeeContainer.class);

2. Set 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);

3. Switch the mock to a reply mode.

EasyMock.replay(coffeeContainer);

EasyMock has a lot of different methods its API is not small, so I recommend you 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 provides convenient way for this.

@RunWith(Suite.class)
@SuiteClasses({ AbstractContainerTest.class, CoffeeMachineTest.class })
public class AllTests {

}

Summary

Unit testing is 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 omitted many interesting moments and techniques which I plan to cover in the following tutorials.
You can download the source code of the tutorial from my DropBox.

About The Author

Mathematician, programmer, wrestler, last action hero... Java / Scala architect, trainer, entrepreneur, author of this blog

Close