Reputation: 26044
A Sample
can be deleted if status is S
or P
. I have this tests:
@Test
public void canBeDeletedWhenStatusIsP() {
Sample sample = new Sample();
sample.setState("P");
assertTrue(sample.canBeDeleted());
}
@Test
public void canBeDeletedWhenStatusIsS() {
Sample sample = new Sample();
sample.setState("S");
assertTrue(sample.canBeDeleted());
}
Should I go further? How should I test when the sample can't be deleted? For example:
@Test
public void cantBeDeletedWhenStatusINeitherPNorS() {
Sample sample = new Sample();
sample.setState("Z");
assertFalse(sample.canBeDeleted());
}
Is this test useful? What about the test naming? Would be this logic tested enough?
Upvotes: 10
Views: 346
Reputation: 48654
We should want our tests to be thorough, so they are likely to detect many classes of bugs. So the simple answer is yes, test the no-op case.
You don't tell what the possible values to the state. Let us assume they must be uppercase English letters, giving 26 states. Your question is then essentially the same as "should I have 26 test cases". That is many, but not prohibitive many. Now imagine a more complex case, for which the state is an int and all int values are possible. Testing them all thoroughly would be impractical. What to do?
The means for dealing with testing when there are very many inputs or initial states is to use equivalence partitiong. Divide the inputs or states into sets of inputs and sets of states, such that all the elements in a set should result in the same behaviour and are adjacent to each other. So in your case the equivalence partitions might be A-O, P, Q-R,S, T-Z. Then have one test case for each partition.
Upvotes: 1
Reputation: 140427
SaintThread is giving you a good "direct" answer.
But lets step back. Because you are doing something wrong in your production code. Most likely, your production code does something like a switch on that String that denotes the sample state. And not only once, but within all the methods it provides. And ... that is not a good OO design!
Instead, you should use polymorphism, like:
abstract class Sample {
boolean canBeDeleted();
// ... probably other methods as well
with and various concrete subclasses, like
class ZSample extends Sample {
@Override canBeDeleted() { return false; }
// ...
And finally, you have
class SampleFactory {
Sample createSampleFrom(String stateIdentifier) {
// here you might switch over that string and return a corresponding object, for example of class ZSample
And then, your tests boil down to:
The point is: your code does do the work of a FSM (finite state machine). Then don't use if/elses all over the place; instead, do the OO thing: create an explicit state machine. And, free bonus: this approach would also make it possible to turn your Sample objects into immutable thingies; which is most often better than having to deal with objects that can change their state over time (as immutability helps a lot with multi-threading issues for example).
Disclaimer: if your "Sample" class is only about that one method, maybe the above is overkill. But in any other case ... maybe step back and see if my suggestions would add value to your design!
Upvotes: 13
Reputation: 4066
In my opinion you should test:
cantBeDeletedWithoutStatus
assertFalse(sample.canBeDeleted());
cantBeDeletedWhenStatusIsInvalid
sample.setState("Z");
assertFalse(sample.canBeDeleted());
cantBeDeletedWhenStatusIsToggledToInvalid
sample.setState("P");
sample.setState("Z");
assertFalse(sample.canBeDeleted());
canBeDeletedWhenStatusIsToggledToS
sample.setState("Z");
sample.setState("S");
assertFalse(sample.canBeDeleted());
canBeDeletedWhenStatusIsToggledToP
sample.setState("Z");
sample.setState("P");
assertFalse(sample.canBeDeleted());
Let me know your thoughts in the comments
Upvotes: 2