3

I'm studying for my first job interviews as Junior Java Developer and right now I'm trying to learn JUnit test cases. This is an example that I encountered and I must say it's really tricky for me (it's abstract code so I have no idea how to test it).

public class JuiceMaker {

  public Juice makeJuice(final List<Fruit> fruits) throws RottenFruitException {
    for (final Fruit fruit : fruits) {
      if (FruitInspector.isFruitRotten(fruit)) {
        throw new RottenFruitException(fruit.getName() + “ is rotten. Cannot make juice.”);
      }
    }

    return Juicer.juice(fruits);
  }
} 

The only example I managed to create myself is this one:

JuiceMaker jm = new JuiceMaker();

@Test
public void isThrowingException() {
//when
  try {
      jm.throwsRuntime();
      Assert.fail("Expected exception to be thrown");
  } catch (RottenFruitException e) {
//then
      assertThat(e)
          .isInstanceOf(RottenFruitException.class)
          .hasMessage((fruit.getName() + " is rotten. Cannot make juice.");
  }
}

Any tips of what kind of tests I can perform on this piece of code? Thanks a lot for your help!

Vinci
  • 341
  • 1
  • 5
  • 18
  • 1
    I think you should ask this question on another stackoverlfow site, like a source review. – M. le Rutte May 16 '17 at 15:55
  • It would be easier to answer your question if you provide the code for Fruit, FruitInspector, Juicer and depending on the impementation Juice. Moreover in your test example you refer to throwsRuntime method which is not defined in JuiceMaker class, so please provide full definition of this class too. The last thing: in your example you are accessing fruit object, but it's not defined. – gawi May 16 '17 at 15:55
  • I do not have any implementation for these methods. That's all I got to work with: "In test driven development tests should document implementation as well as testing it. Having this in mind, write tests for this legacy code. Then refactor to remove implicit dependencies." – Vinci May 16 '17 at 16:00
  • You create three lists: one with only rotten food, one with only fresh food and one with both. Assert that `makeJuice` returns a `Juice` for the fresh food, and assert that an exception will be thrown for the other two. – Vince May 16 '17 at 16:12

3 Answers3

3

Welcome to JUnit, and good luck with your interviews!

First question to ask is what is the contract offered by this class? It takes a list of fruit, tests if any of the fruit are rotten, if so it throws an exception, otherwise it juices them. You can assume the "juice" method is tested elsewhere.

To me, that suggests these tests:

  • List with single good fruit
  • List with single rotten fruit
  • List with several good and one rotten fruit
  • Empty list

You could also test for null and invalid values, but that might be overdoing things just now.

Once you've decided what to test, then you can start thinking about implementing them. Looks like your implementation has a couple of errors, but you're heading in a good direction. You might find JUnit's "expected" parameter useful for testing for exceptions.

Community
  • 1
  • 1
hugh
  • 2,237
  • 1
  • 12
  • 25
2

You seem to be instructing the JuiceMaker instance in your test to throw the exception to verify you can catch it.

You have to answer yourself whether that alone will exercise the loop iterating through the list of Fruit and the if() statement.

You can influence the JuiceMaker.makeJuice() better by passing different lists (null, empty, with no rotten fruit, with rotten fruit).

This way you are not forcing any exceptions but causing them - which exercises more paths through your code under test.

If you exercise the above scenarios, you should have a very decent test coverage of your method.

Hope this helps!

diginoise
  • 7,352
  • 2
  • 31
  • 39
2

The two testcases you put up in your example-answer are going in the right direction, but only half way. Because both tests do not at all test your "class under test".

Reasonable tests would more look like:

public class JuiceMakerTest {
  private JuiceMaker underTest;

  @Before
  public void setup() { underTest = new JuiceMaker; }

  @Test(expected=RottenFruitException.class)
  public void testThrowsOnRottenFruit() {
    underTest.makeJuice(Collections.singletonList("rotten apple"));
  }

  @Test(expected=???)
  public void testWithNullList() {
    underTest.makeJuice(null);
  }

  @Test(expected=???)
  public void testWithEmptyList() {
    underTest.makeJuice(Collections.emptyList());
  }

  @Test
  public void testXyz() {
   Juice expectedResult = ...
   assertThat(underTest.makeJuice(Collections.singletonList("apple")), is(expectedResult);
  }

and so on. In other words: you follow the nice answer from hugh; and determine the possible ways to invoke your method. The ??? is just a place holder - indicating that you should think up what should happen here. Maybe you expect a specific exception; maybe the method returns a special empty juice ... all up to the contract of the method under test.

You derive error conditions from that, and expected results. And then you write at least one test for of the different aspects you collected.

GhostCat
  • 137,827
  • 25
  • 176
  • 248