You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DataJpaTest单测全过批量运行失败:列表类型不匹配问题

JpaRepository测试批量运行失败,单独运行通过

问题现象

每个测试单独运行都能通过,但批量运行所有测试时,shouldFindSingleRecipeByName()测试失败,错误信息如下:

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)"

测试类代码

@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实体类代码

@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();
    }
} 

问题原因及解决方案

1. List实现类不一致导致断言失败

List.of()返回的是JDK内部不可变列表(如List12),而JPA查询返回的是ArrayList。虽然List规范要求equals仅比较内容,但批量运行时测试上下文的差异会导致AssertJ的isEqualTo断言因实例类型不同而失败。

解决方法:
改用AssertJ的内容断言方法,无需关心List的实现类:

// 替换原有的isEqualTo断言
assertThat(recipesList).hasSize(1).containsExactly(recipe1);

或者显式将预期列表转为ArrayList:

assertThat(recipesList).isEqualTo(new ArrayList<>(List.of(recipe1)));

2. 数据库自增序列未重置导致实体ID不匹配

@BeforeEach中的deleteAll()仅删除数据,不会重置数据库自增序列。批量运行时,后续测试的实体ID会从之前的最大值继续递增,而测试代码中手动设置的ID(如1L、2L)与数据库实际生成的ID不一致,导致依赖ID的equals比较失败。

解决方法:

  • 不手动设置ID,保存后获取实际持久化的实体进行断言:
// 保存后获取真实的持久化实体
Recipe savedRecipe1 = recipeRepositoryUnderTest.save(recipe1);
Recipe savedRecipe2 = recipeRepositoryUnderTest.save(recipe2);

// 使用savedRecipe1进行断言
assertThat(recipesList).containsExactly(savedRecipe1);
  • 或者在@BeforeEach中添加重置自增序列的SQL(以H2数据库为例):
@Autowired
private JdbcTemplate jdbcTemplate;

@BeforeEach
void tearDown() {
    recipeRepositoryUnderTest.deleteAll();
    // 重置自增序列
    jdbcTemplate.execute("ALTER TABLE Recipes ALTER COLUMN id RESTART WITH 1");
}

内容的提问来源于stack exchange,提问作者user10937817

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 08:25:21