-1

I have a dead simple test case with the most basic set up:

@Entity
public class Tags {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(unique = true)
  private String name;

  public Tags(String name) {
    this.name = name;
  }
}

@Repository
public interface TagsRepository extends JpaRepository<Tags, Integer> {}

Test set up:

@DataJpaTest
public class TagsTest implements WithAssertions {

  @Autowired
  private TagsRepository tagsRepo;

  @BeforeEach
  void setUp() {
    Tags tag1 = new Tags("tag1");
    Tags tag2 = new Tags("tag2");
    Tags tag3 = new Tags("tag3");

    tagsRepo.saveAll(List.of(tag1, tag2, tag3));
  }

  @AfterEach
  void cleanUp() {
    tagsRepo.deleteAll();
  }

  @Test
  void test() {
    Tags actual1 = tagsRepo.findById(1).orElse(null);
    assertThat(actual1).isNotNull();  // <- passed
  }

  @Test
  void test2() {
    Tags actual2 = tagsRepo.findById(1).orElse(null);
    assertThat(actual2).isNotNull();  // <- failed
  }
  
  @Test
  void test3() {
    Tags actual3 = tagsRepo.findById(2).orElse(null);
    assertThat(actual3).isNotNull();  // <- failed
  }
}

Unexpected behavior:

When run test class as a whole, only the first test passed and second and third test all failed.

And this leads to problem in @ParameterizedTest as well:

@ParameterizedTest
@ValueSource(ints = { 1, 1, 2 }) // <- second and third test failed
void test4(Integer id) {
  Tags actual = tagsRepo.findById(id).orElse(null);
  assertThat(actual).isNotNull();
}

Same behavior after changing dataSource to @MethodSource or @CsvSource.

I tried to initialize data in each test, but same thing happend:

@Test
void test() {
  Tags tag1 = new Tags("tag1");
  Tags tag2 = new Tags("tag2");
  Tags tag3 = new Tags("tag3");
  
  tagsRepo.saveAll(List.of(tag1, tag2, tag3));
  
  Tags actual1 = tagsRepo.findById(1).orElse(null);
  
  assertThat(actual1).isNotNull();
  
  tagsRepo.deleteAll();
}

Why would only the first test passed and subsequent test failed? I've tried using different combinations of id passed in. But all the same behavior.

With this simple demo, I came to the conclusion that, running similar test will casue issue, even though, test should not influence each other?

I know I shouldn't test the built-in API provided by SpringBoot out of the box, but this is a issue I discovered when testing mybatis with mutiple params using @Parameterized.

Edit: Seems like most folks tried to solve this by re-creating database before each test

Enfield Li
  • 1,952
  • 1
  • 8
  • 23
  • You create 3 tags for every test **and then delete them again**. The ids will **not** be reset. Meaning the first test has tags with ids 1,2,3 while the second will have 4,5,6, and the third will have 7,8,9. So no wonder your second and third test fail. There is *no* tag with id `2` resp. `3` – Lino Jul 20 '22 at 16:47
  • Yeah, thanks for answering, I wasn't expecting the reason to be this simple... However, even if I don't delete them manually in `@AfterEach`, shouldn't the data still there? While it continues to run second and third tests? Does `Junit` delete the data after each test? I'm using h2 as database. I tried to figure out a way to do the `@ParameterizedTest`, if everytime data got deleted and index increased, how am I suppose to test... I'm new in Junit testing. – Enfield Li Jul 20 '22 at 16:55

1 Answers1

1

It doesn't work because the counter for the ID goes on and on every time you are deleting/saving the data in your setUp() method:

@BeforeEach
  void setUp() {
    Tags tag1 = new Tags("tag1");
    Tags tag2 = new Tags("tag2");
    Tags tag3 = new Tags("tag3");

    tagsRepo.saveAll(List.of(tag1, tag2, tag3));
  }

for the first test you will have:

tag1-> id = 1
tag2-> id = 2
tag3-> id = 3

but for the second one you will have:

tag1-> id = 4
tag2-> id = 5
tag3-> id = 6

and so on... so the right way would be to save each of them and keep in a class level variable, so something like this:

@DataJpaTest
public class TagsTest implements WithAssertions {

    @Autowired
    private TagsRepository tagsRepo;

    private Tags tag1;
    private Tags tag2;
    private Tags tag3;
    
    @BeforeEach
    void setUp() {
        tag1 = tagsRepo.save(new Tags("tag1"));
        tag2 = tagsRepo.save(new Tags("tag2"));
        tag3 = tagsRepo.save(new Tags("tag3"));
    }

    @AfterEach
    void cleanUp() {
        tagsRepo.deleteAll();
    }

    @Test
    void test() {
        Tags actual1 = tagsRepo.findById(tag1.getId()).orElse(null);
        assertThat(actual1).isNotNull();
    }

    @Test
    void test2() {
        Tags actual2 = tagsRepo.findById(tag1.getId()).orElse(null);
        assertThat(actual2).isNotNull();
    }

    @Test
    void test3() {
        Tags actual3 = tagsRepo.findById(tag2.getId()).orElse(null);
        assertThat(actual3).isNotNull();
    }
}

Regarding the parametrized test, you cannot really know the id in advance using an auto incremented id

cdr89
  • 958
  • 9
  • 18
  • 1
    Keep in mind, that there might not be the execution order you expect "By default, test classes and methods will be ordered using an algorithm that is deterministic but intentionally nonobvious. This ensures that subsequent runs of a test suite execute test classes and test methods in the same order, thereby allowing for repeatable builds." [Test Execution Order](https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order) – Timo Jul 20 '22 at 17:20