'How can I conduct a JUnit test which relies on the operation of another method?

I wish to conduct a JUnit-4 test for a method that reverts the effect of another method such that the test won't just pass if the first method is broken but instead requires both to work correctly. How can I address this?

Consider my following code...

    @Test
    public void testRemoveCategory(){
        Category newCategory = new Category();

        testRecipe.addCategory(newCategory);
        testRecipe.removeCategory(newCategory);
        assertFalse(testRecipe.getCategories().contains(newCategory));

In this code, the method removeCategory(Category category) can logically only remove a Category if the method addCategory(Category category)\ has already added it. This creates two problems

  1. addCategory must work for removeCategory to pass the test
  2. If addCategory doesn't work, removeCategory WILL pass the test even if it doesn't work either.

I cannot think of a solution to the first problem, but I solved the second problem by using the following code...

    @Test
    public void testRemoveCategory(){
        Category newCategory = new Category();
        boolean passesTest;


        testRecipe.addCategory(newCategory);
        if (!testRecipe.getCategories().contains(newCategory)){
            passesTest = false;
        }
        else
        {
            testRecipe.removeCategory(newCategory);
            if(!testRecipe.getCategories().contains(newCategory))
            {
                passesTest = true;
            }
            else
            {
                passesTest = false;
            }
        }

        assertTrue(passesTest);
    }

This however,

  1. is clearly a bad workaround.
  2. still does not solve the first problem.

I can see this general situation occurring often as many methods do serve as inverses of each other. How can I address these issues?



Solution 1:[1]

Unit tests are focused on only one unit's behaviour. testRemoveCategory should not bother if addCategory is working correctly. You may add new categories by reflection, by other methods such as addCategories. You can not test all permutations of the methods.

I do not know the implementation details of how Recipe stores the categories. If the category holder is not passed by constructor or injected, you may try the approach below:

  • Define the initial state of unit under the test at given block
  • Try to change the state of unit at when block
  • And then, Assert the final state
@Test
public void testRemoveCategory() {
        // given
        Category otherCategory = new Category();
        Category newCategory = new Category();
        
        testRecipe.addCategory(otherCategory);
        testRecipe.addCategory(newCategory);

        // at this point testRecipe should have only two categories: newCategory and otherCategory
        // you may add the categories by any other way not only addCategory()
        // we are not testing the behaviour of addCategory

        // when
        testRecipe.removeCategory(newCategory);

        // then
        // testing the absence of newCategory
        assertFalse(testRecipe.getCategories().contains(newCategory));

        // testing the presence of otherCategory
        assertTrue(testRecipe.getCategories().contains(otherCategory));

        // !! but not testing only size of categories
        // which may be flaky if there is a bug in removeCategory()
        // assertTrue(testRecipe.getCategories().size(), 1);
}

Finally, If and else's are red flags in unit tests. One should never have to use it.

Hope, it helps.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ocos