package suitcaseisolationtesting;The fake class is very minimal - it doesn't even have a list.
import java.util.ArrayList;
/**
* SuitcasePacker represents a tool that can fill a suitcase
* from a list of desired items to take traveling.
* In this version, the "Fake" dependency name is hardcoded into the constructor,
* which is a simple approach, but has the downside of requiring recompiling the
* class to replace the name with the real dependency for integration testing.
*
*/
public class SuitcasePacker
{
private FakeWeightConstrainedList suitcase;
private ArrayList<TravelGear> packableItems;
/** Constructor for objects of type Suitcase */
public SuitcasePacker(ArrayList<TravelGear> desiredItems)
{
suitcase = new FakeWeightConstrainedList();
packableItems = desiredItems;
}
/** Attempt to put an item in the suitcase, subject to a weight limit.
* @param position the zero-based index of the item to be moved.
* @return true if the item was added to the suitcase, false if adding
* the item would have exceeded the current weight limit.
*/
public boolean addToCase(int position)
{
// Is there room in the suitcase for another item?
if (suitcase.notFull(packableItems.get(position)))
{
suitcase.add(packableItems.remove(position));
return true;
}
return false;
}
/** Returns the number of items in the suitcase.
* @return the number of items in the suitcase.
*/
public int numItems()
{
return suitcase.size();
}
}
The unit test cleverly sets up the test data to correspond to what the fake is expecting.
package suitcaseisolationtesting;
import java.io.*;
/**
* Fake implementation to support unit testing.
*/
public class FakeWeightConstrainedList
{
private int size;
public FakeWeightConstrainedList()
{
super();
size = 0;
}
public boolean notFull(TravelGear newItem)
{
return newItem.getWeight() == 1;
}
public boolean add(TravelGear gear)
{
size = 1;
return true;
}
public int size()
{
return size;
}
}
public void testAddToCase()The advantage of this approach is that a carefully designed unit test will also pass when run during integration. The downside is that it requires recompiling SuitcasePacker for integration testing to replace the fake name with the real dependency. This is a reasonably serious drawback because it precludes a full automated test suite. You can't run all the unit tests and all the integration tests at the same time.
{
System.out.println("unit test OR integration test of addToCase");
ArrayList<TravelGear> desiredGear = new ArrayList<TravelGear>();
desiredGear.add(new TravelGear("A",1)); // position zero
desiredGear.add(new TravelGear("B",1)); // one
desiredGear.add(new TravelGear("C",46)); // two
SuitcasePacker packer = new SuitcasePacker(desiredGear);
// check that suitcase is empty
assertEquals(0,packer.numItems());
// gear in position 2 is too big
assertFalse(packer.addToCase(2));
// add a gear that fits
assertTrue(packer.addToCase(1));
// Verify suitcase now has one gear
assertEquals(1,packer.numItems());
assertEquals(2,desiredGear.size());
// verify the proper gear was removed from desiredGear
assertEquals("A",desiredGear.get(0).toString());
assertEquals("C",desiredGear.get(1).toString());
}
package mixtapeisolationtesting;Here's how the constructor is modified:
/**
* Interface for a list that is constrained by a weight limit.
* Items can be added and removed from the list and the list
* automatically tracks the available weight left in the list.
*
* @author jdalbey
*/
public interface I_WeightConstrainedList
{
/**
* Default weight limit in pounds
*/
int kDefaultWeightLimit = 45;
/**
* Accessor to the available weight remaining in the list,
* that is, unused weight.
* @return remaining weight
*/
int getRemainingWeight();
/**
* Adds up the total weight taken by the items in this list.
* @return int total weight
*/
int getTotalWeight();
/**
* Determine if the list has room for a specific piece of gear.
* I.e., if this item were added to the list, would it
* exceed the list's capacity?
* @ return true if the item can fit, false otherwise.
*/
boolean notFull(TravelGear newItem);
/**
* Appends the specified element to the end of this list.
* @param newItem - element to be appended to this list.
* @return true (as per the general contract of Collection.add).
*/
boolean add(TravelGear newItem);
/**
* Returns the number of elements in this list.
* @return the number of elements in this list.
*/
int size();
}
private I_WeightConstrainedList suitcase;Now the unit test creates an instance of the fake and passes it to the class under test:
private ArrayList<TravelGear> packableItems;
/** Constructor for objects of type Suitcase */
public SuitcasePacker(ArrayList<TravelGear> desiredItems, I_WeightConstrainedList bag)
{
suitcase = bag;
packableItems = desiredItems;
}
I_WeightConstrainedList fake = new FakeWeightConstrainedList();A separate integration test creates an instance of the real dependent class and passes it to the class under test:
SuitcasePacker packer = new SuitcasePacker(desiredGear,fake);
I_WeightConstrainedList real = new WeightConstrainedList();Now if a second developer needs a slightly different fake, they can create a private inner class in their unit test that overrides the methods they want to customize.
SuitcasePacker packer = new SuitcasePacker(desiredGear,real);
public void testAddToCase()Zip file for second example code
{
System.out.println("unit test ONLY of addToCase");
ArrayList<TravelGear> desiredGear = new ArrayList<TravelGear>();
desiredGear.add(new TravelGear("A",1)); // position zero
desiredGear.add(new TravelGear("B",10)); // one
desiredGear.add(new TravelGear("C",46)); // two
// Replace this with the real class for integration testing.
I_WeightConstrainedList fake = new Fake2WeightConstrainedList();
SuitcasePacker packer = new SuitcasePacker(desiredGear,fake);
...
}
class Fake2WeightConstrainedList extends FakeWeightConstrainedList
{
public boolean notFull(TravelGear newItem)
{
return newItem.getWeight() == 10;
}
}
package suitcaseisolationtesting;
import java.util.ArrayList;
import junit.framework.TestCase;
import org.jmock.Mockery;
import org.jmock.Expectations;
public class SuitcasePackerUnitTest extends TestCase {
Mockery context = new
Mockery();
public SuitcasePackerUnitTest(String testName) {
super(testName);
}
@Override
protected void setUp() throws Exception {
super.setUp();
}
public void testAddToCase()
{
System.out.println("unit
test ONLY of addToCase");
ArrayList<TravelGear>
desiredGear = new ArrayList<TravelGear>();
final TravelGear item0 = new
TravelGear("A",1);
final TravelGear item1 = new
TravelGear("B",1);
final TravelGear item2 = new
TravelGear("C",46);
desiredGear.add(item0); //
position zero
desiredGear.add(item1); //
one
desiredGear.add(item2); //
two
final
I_WeightConstrainedList fake =
context.mock(I_WeightConstrainedList.class);
SuitcasePacker packer = new
SuitcasePacker(desiredGear,fake);
context.checking(new
Expectations() {{
oneOf
(fake).size();
will(returnValue(0));
oneOf
(fake).notFull(item2);
will(returnValue(false));
oneOf
(fake).notFull(item1);
will(returnValue(true));
oneOf
(fake).add(item1);
will(returnValue(true));
oneOf
(fake).size();
will(returnValue(1));
never
(fake).add(item0);
will(returnValue(true));
never
(fake).add(item2);
will(returnValue(false));
}});
// check that playlist size
is empty
assertEquals(0,packer.numItems());
// item in position 2 is too
big
assertFalse(packer.addToCase(2));
// add an item that fits
assertTrue(packer.addToCase(1));
// Verify suitcase now has
one item
assertEquals(1,packer.numItems());
assertEquals(2,desiredGear.size());
// verify the proper item
was removed from desiredGear
assertEquals("A",desiredGear.get(0).toString());
assertEquals("C",desiredGear.get(1).toString());
context.assertIsSatisfied();
}
}
Zip
file
for
JMock
version
package suitcaseisolationtesting;
import java.util.ArrayList;
import junit.framework.TestCase;
import static org.mockito.Mockito.*;
public class SuitcasePackerTest extends TestCase {
public SuitcasePackerTest(String testName) {
super(testName);
}
ArrayList<TravelGear> desiredGear;
/**
* Test of addToCase method, of class SuitcasePacker.
*/
public void testAddToCase()
{
System.out.println("test addToCase");
desiredGear = new ArrayList<TravelGear>();
final TravelGear item0 = new TravelGear("A",1);
final TravelGear item1 = new TravelGear("B",1);
final TravelGear item2 = new TravelGear("C",46);
desiredGear.add(item0); // position zero
desiredGear.add(item1); // one
desiredGear.add(item2); // two
WeightConstrainedList gearlist = mock(WeightConstrainedList.class);
SuitcasePacker packer = new SuitcasePacker(desiredGear,gearlist);
// check that playlist size is empty
when(gearlist.size()).thenReturn(0);
assertEquals(0,packer.numItems());
// item in position 2 is too big
when(gearlist.notFull(item2)).thenReturn(false);
assertFalse(packer.addToCase(2));
// add an item that fits
when(gearlist.notFull(item1)).thenReturn(true);
assertTrue(packer.addToCase(1));
// Verify suitcase now has one item
when(gearlist.size()).thenReturn(1);
assertEquals(1,packer.numItems());
assertEquals(2,desiredGear.size());
// verify the proper item was removed from desiredGear
assertEquals("A",desiredGear.get(0).toString());
assertEquals("C",desiredGear.get(1).toString());
}
}