1
votes

I might be asking something completely obvious. I do not have much experience in writing unit tests, and the following question came up which got me thinking.

Say, you have a class and this class has methods you want to test. But can you test a single method at once? I think not. In order to test the method, you need to invoke one or more other methods. For example:

class MyClass { 
    int x;
    void foo() { x = 4; }
    boolean bar() { x = 3; }
    boolean check() { return x == 4; }
}

In order to test foo and bar, I need to use check() and on the other hand, in order to test check I need to use either foo() or bar().

Say, I have to following test case:

class MyClassTest {
    @Test
    void testFoo() {
        MyClass obj = new MyClass();
        obj.foo();
        assert obj.check();
    }
}

Now let's assume, my colleague changes the check() method:

boolean check() { return x == 5; }

Of course, the testFoo() will fail, and one might think that there is a problem with the foo method.

So this looks like a chicken-egg situation. How do people usually resolve this?

3
What is the unit of work? That's generally the scope at which it's applicable to test... sometimes it's as neat as a single method, but frequently you need to create a "fixture" or "suite" to define your "unit of work". - Elliott Frisch

3 Answers

5
votes

It probably doesn't make much sense to test methods independently here because the state of the class can change according to the sequence of methods called. In this case I'd make sure the behavior - or state transitions - work as expected. Please refer to the following three tests which properly specify the behavior of the class:

class WhenFooWasCalled {
    @Test
    public void ThenCheckShouldReturnTrue {
        MyClass sut = new MyClass();
        sut.foo();
        assertTrue(sut.check());
    }
}

class WhenBarWasCalled {
    @Test
    public void ThenCheckShouldReturnFalse {
        MyClass sut = new MyClass();
        sut.bar();
        assertFalse(sut.check());
    }
}

class WhenNothingWasCalled {
    @Test
    public void ThenCheckShouldReturnFalse {
        MyClass sut = new MyClass();
        assertFalse(sut.check());
    }
}

Updated: I added a third test case: How does the check() behave if neither foo() nor bar() were called.

2
votes

You don't write tests for individual methods - you write tests for individual requirements. In this case your class seems to have two requirements:

  1. "check must return true when foo was called last"
  2. "check must return false when bar was called last"

So you would write two unit-tests, one for each requirement to verify that the class fulfills it correctly (you might want to formulate a third requirement: what check is supposed to do when neither was called). When your colleague made the change above, he broke neither the foo method, not the check method. What he broke was the first requirement. Now he has to change the class somehow so that it fulfills it again. How he accomplishes this (change foo, change checkor both) doesn't matter.

1
votes

This is common with a class with internal mutable state. I think your question reduces to your unit tests behaving properly after a given sequence of state manipulations.