1

I have just started learning JUnit very recently and came across the following problem.

Have a look at the following class

class MyClass {
  String a;

  public MyClass(String a) {
    this.a=a;

  String doSomething(String a) {
    if( a.isEmpty() )
      return "isEmpty";
    else
      return"isNotEmpty";
}

I want to test the above method for both the conditions. If I proceed with the general structure of writing testcases it will look something like this:

class MyClassTest {

  MyClass myClass;

  @BeforeEach
  void setUp() {
    myClass=new MyClass("sampleString");
  }

  @Test
  void doSomethingTest() {
    Assertions.equal("isNotEmpty", myClass.doSomething());
  }
}

However, for testing the empty string condition I will need another setup method where instead of "sampleString" I pass an empty string.

Following are the approaches I could think of and the questions for each:

  • Not use setUp at all and instead initialize the class in the individual test method. However, if let's say there are 10 testcases; 5 of which require empty string and rest "sampleString" then this doesn't make sense. Again, we can have a separate method for this repetitive code and call it individually in each testcase but then that defeats the purpose of having a steup method. Lets say I wanted to use two different setup methods, is there a way to do so?
  • Have a parameterized setup. I don't know if this is possible though. If yes, please share some useful links for this.
  • Use TestFactory. I tried reading up about this, but couldn't find an example for this specific case. If you have any, please share.

This example has been kept simple for illustrative purposes.

kernel0707
  • 314
  • 2
  • 11

2 Answers2

2

Group the tests with the same setup in an inner class annotated with @Nested. Each nested test class can have its own setup in a local @BeforeEach method.

johanneslink
  • 4,877
  • 1
  • 20
  • 37
1

You can always prepare the non-common data inside your test method. I've always thought it's easier this way, compared to using parameterized tests. You can't mix parameterized and non-parameterized tests in 1 file.

@Test
void doSomething_nullString()
{
    myClass = new MyClass(null);
    Assert.assertNull(myClass.doSomething());
}

@Test
void doSomething_emptyString()
{
    myClass = new MyClass("");
    Assert.assertEquals("", myClass.doSomething());
}

@Test
void doSomething_nonEmptyString()
{
    myClass = new MyClass("sampleString");
    Assert.assertEquals("sampleString", myClass.doSomething());
}

Or, you can always have helper methods inside the test class.

private MyClass createTestObject_nonNullString() {
    return new MyClass("nonNullString");
}

private MyClass createTestObject_nullString() {
    return new MyClass(null);
}

@Test
public void doSomething_sample() {

    MyClass test = createTestObject_nonNullString();
    // perform test
}
geffchang
  • 3,279
  • 2
  • 32
  • 58
  • This is useful if the constructor initializations and setup is simple. However, consider a case where the class used for 5 tests has the same constructor and setup steps; and the remaining 5 tests have a similar class and setup. What should be the approach then? – kernel0707 Apr 22 '20 at 12:19
  • @kernel0707 I've updated my answer. Maybe there are better approaches, but this is how I usually do it. – geffchang Apr 22 '20 at 12:20
  • I had a look at your solution after submitting this comment. Editing and keeping it for others to see. Above solution works. :) What if the class has several methods and each behaves differently based on whether the string is empty or not. How to test both sets of behavior? Another real-life example could be let's say I have a class with an array as a member variable. Each method of this class gives some output based on the array and throws an expcetion if array is empty. So how should I test each method first for the general behavior, and then for the case where the exception is thrown. – kernel0707 Apr 22 '20 at 12:23
  • @kernel0707 I can see your excitement, but I'm not exactly sure how to answer all your question. I would suggest to find some online course about JUnit, so you can learn the basics of unit testing. Later, you will need to use other libraries like Mockito (or EasyMock), which will help you mock the behavior of dependencies/interfaces that are used in your code. For exceptions, there's a way to test that too - like this: https://www.guru99.com/junit-exception-test.html – geffchang Apr 22 '20 at 12:38
  • Why can’t you mix parameterized with normal tests in one file? Although I don’t see how parameterized would test help for the given question at all. – johanneslink Apr 23 '20 at 05:37
  • @johanneslink He was considering parameterized tests. Also, TIL there is Enclosed which allows you to mix parameterized and non-parameterized, but it's an experimental feature: https://stackoverflow.com/questions/32776335/when-using-junits-parameterized-can-i-have-some-tests-still-run-only-once – geffchang Apr 23 '20 at 05:43