user10937817
user10937817

Reputation:

DataJpaTest - every single test passes but when run all together 1/3 fails

Hello I wanted to test my method from an interface that extends from JpaRepository but I've go a strange behaviour. Every single test passes but when I run them all together the test named shouldFindSingleRecipeByName() doesn't pass and the error is:

expected: "[com.example.recipesapi.model.Recipe@45f421c] (List12@14983265)"
 but was: "[com.example.recipesapi.model.Recipe@45f421c] (ArrayList@361483eb)"
org.opentest4j.AssertionFailedError: 
expected: "[com.example.recipesapi.model.Recipe@45f421c] (List12@14983265)"
 but was: "[com.example.recipesapi.model.Recipe@45f421c] (ArrayList@361483eb)"

I get that there is difference between expected and but was but I don't understand why when i run single test it passes and how to make it pass all together.

@DataJpaTest
class RecipeRepositoryTest {

    @Autowired
    private RecipeRepository recipeRepositoryUnderTest;

    @BeforeEach
    void tearDown() {
        recipeRepositoryUnderTest.deleteAll();
    }

    @Test
    void shouldFindSingleRecipeByName() {
        //given
        String searchName = "Tomato soup";

        Recipe recipe1 = new Recipe(
                1L,
                "Tomato soup",
                "Delicious tomato soup",
                Arrays.asList("1. ", "2. "),
                Arrays.asList("1. ", "2. ")
        );

        Recipe recipe2 = new Recipe(
                2L,
                "Mushrooms soup",
                "Delicious mushrooms soup",
                Arrays.asList("1. ", "2. "),
                Arrays.asList("1. ", "2. ")
        );

        List<Recipe> recipes = List.of(recipe1, recipe2);

        recipeRepositoryUnderTest.saveAll(recipes);

        //when
        List<Recipe> recipesList = recipeRepositoryUnderTest.findRecipeByName(searchName.toLowerCase());

        //then
        assertThat(recipesList).isEqualTo(List.of(recipe1));
    }

    @Test
    void shouldFindTwoRecipesByName() {
        //given
        String searchName = "oup";

        Recipe recipe1 = new Recipe(
                1L,
                "Tomato soup",
                "Delicious tomato soup",
                Arrays.asList("1. ", "2. "),
                Arrays.asList("1. ", "2. ")
        );

        Recipe recipe2 = new Recipe(
                2L,
                "Mushrooms soup",
                "Delicious mushrooms soup",
                Arrays.asList("1. ", "2. "),
                Arrays.asList("1. ", "2. ")
        );

        List<Recipe> recipes = List.of(recipe1, recipe2);

        recipeRepositoryUnderTest.saveAll(recipes);

        //when
        List<Recipe> recipesList = recipeRepositoryUnderTest.findRecipeByName(searchName.toLowerCase());

        //then
        assertThat(recipesList).isEqualTo(List.of(recipe1, recipe2));
    }

    @Test
    void findByNameShouldReturnEmptyListOfRecipes() {
        //given
        String searchName = "Tomato soup";

        //when
        List<Recipe> recipesList = recipeRepositoryUnderTest.findRecipeByName(searchName.toLowerCase());

        //then
        assertThat(recipesList).isEqualTo(List.of());
    }

}

Recipe class code:

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Entity
@Table(name = "Recipes")
public class Recipe {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @NotNull
    @NotEmpty
    private String name;

    @NotNull
    @NotEmpty
    @NotBlank
    private String description;

    @ElementCollection
    private List<String> ingredients;

    @ElementCollection
    private List<String> directions;

    @Override
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
        final Recipe recipe = (Recipe) o;
        return id != null && Objects.equals(id, recipe.id);
    }

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
} 

Upvotes: 0

Views: 331

Answers (3)

IlyaP
IlyaP

Reputation: 397

In my case, it was this:

  1. ok, each @Test method in @DataJpaTest has @Transactional annotation so it starts a new transaction and rolls it back after test method finishes.
  2. But what if body of your test method starts calling some other methods of your own domain which in turn are also marked as @Transactional, but, unlike the upper one, that @Transactional is marked with propagation=REQUIRES_NEW? This is the problem - REQUIRES_NEW will open entirely new, unrelated transaction, which will be committed before your test method finishes - and so rollback of the parent transaction will have absolutely no effect on objects created/modifed/committed by that new internal transaction.

So to fix it, you have to either reconsider using REQUIRES_NEW for the inner transaction or to clean up manually in @AfterEach test methods.

Upvotes: 0

Oleksii Zghurskyi
Oleksii Zghurskyi

Reputation: 4365

expected: "[com.example.recipesapi.model.Recipe@45f421c] (List12@14983265)" but was: "[com.example.recipesapi.model.Recipe@45f421c] (ArrayList@361483eb)"

Have you tried using .containsExactly or .containsExactlyInAnyOrder instead of .isEqualTo?

Alternatively, try wrapping the expected value into ArrayList.

Upvotes: 1

MJG
MJG

Reputation: 409

Perhaps your tests run in parallel, thus the data might be shared in between multiple tests could lead to unexpected results, depending on the current state of the repository.

Try to print out each of the elements in the actual and expected lists to verify.

Upvotes: 0

Related Questions