Issue
I have a class Inventory which has 6 methods. I am supposed to implement tests for each on of these 6 methods in a test class called InventoryTEST. The class object is a hashmap.
Here are the methods for the class
/**
* Return the number of Records.
*/
public int size() { }
/**
* Return a copy of the record for a given Video.
*/
public Record get(VideoObj v) { }
/**
* Return a copy of the records as a collection.
* Neither the underlying collection, nor the actual records are returned.
*/
public Collection toCollection() {}
/**
* Add or remove copies of a video from the inventory.
* If a video record is not already present (and change is
* positive), a record is created.
* If a record is already present, <code>numOwned</code> is
* modified using <code>change</code>.
* If <code>change</code> brings the number of copies to be less
* than one, the record is removed from the inventory.
* @param video the video to be added.
* @param change the number of copies to add (or remove if negative).
* @throws IllegalArgumentException if video null or change is zero
* @postcondition changes the record for the video
*/
public void addNumOwned(VideoObj video, int change) {
}
/**
* Check out a video.
* @param video the video to be checked out.
* @throws IllegalArgumentException if video has no record or numOut
* equals numOwned.
* @postcondition changes the record for the video
*/
public void checkOut(VideoObj video) {
}
/**
* Check in a video.
* @param video the video to be checked in.
* @throws IllegalArgumentException if video has no record or numOut
* non-positive.
* @postcondition changes the record for the video
*/
public void checkIn(VideoObj video) {
}
/**
* Remove all records from the inventory.
* @postcondition <code>size() == 0</code>
*/
public void clear() {
}
Now in order to test these methods I will need to create an object with some records in it. One way I could do this is call the default constructor which will give me an empty hashmap and then call addNumOwned to add records to the hashmap. Another way I could do this is create an overloaded constructor which can add records in the hashmap at the time of creation.
The issue I see with the first method is addNumOwned is a method (and unit) being tested in the test class. So if this unit fails all the other ones will as well. Should a unit test be suspectible to the failure of another unit test?
I suppose if I had to make a unit test for the constructor the same argument would hold. I do not have a unit test for the constructor, however.
Solution
If you are writing a suite of tests for a class, it is OK for the set up code of some of your tests to use methods of that class. In fact, it is practically impossible to avoid doing that.
If you want your tests to be comprehensive, you will want to test the constructor of your class. But of course all the tests of the methods of your class will have to first create an object of your class, as part of their set up phase.
public ThingTest // We have a class named Thing we want to test, this is its unit tests
{
@Test
public void testIncrement() // Test of the Thing.increment method
{
// Set up:
var thing = new Thing(); // Using part of the Thing class in the set up phase!
// Exercise:
thing.increment();
// Verify:
assertEquals(1, thing.count());
}
...
You are worried, I guess, because you want your tests to be arranged so that a bug in the code you are testing causes only one test failure, so debugging the test failure is easy. That is an ideal, worth striving for, but it is only an ideal. In practice, some kinds of bugs and tests can not be so simple.
You can mitigate this difficulty by ensuring you have comprehensive tests, and by testing bottom-up. That is, it the set up phase of a test relies on a particular mutator or constructor operating correctly, you must have unit tests of that mutator or constructor.
For complicated code, you can consider using Java level assert
statements (my preference) or JUnit assumptions to check that the set up phase of a test has completed correctly before exercising the method you are testing. Then instead of having a difficult to debug test failure, you will get an AssertionError
or AssumptionViolatedException
or TestAbortedException
that more clearly points towards the cause of the problem.
@Test
public void testIncrement()
{
var thing = new Thing();
assert thing.count() == 0;
thing.increment();
// Verify:
assertEquals(1, thing.count());
}
...
Answered By - Raedwald
Answer Checked By - Mary Flores (JavaFixing Volunteer)