【问题标题】:DataJPATest Junit method naming and NullPointerExceptionsDataJPATest Junit 方法命名和 NullPointerExceptions
【发布时间】:2017-11-10 21:34:10
【问题描述】:

我正在使用 SpringBoot/Kotlin/JPA/Hibernate/Junit 并有 JpaServiceTest 类来练习与单个实体相关的存储库方法。 JpaService 类的方法名遵循findByXXXXIdfindAllupdateXXXXaddXXXXdeleteXXXX 的约定。

为了保持一致性,我使用相同的约定命名 JpaTest 类中的方法。我的 JpaTest 类有两个 findById 场景,一个是“空”,另一个是返回映射实体。我的应用程序按预期工作,但是我的测试类在预期返回有效实体的 findById 场景中失败。

The service class

@Service("MyService")
@Transactional
internal class JpaMyService(val MyRepo: MyRepository) : MyService {

val log = LoggerFactory.getLogger("MyService")

override fun findByMyId(MyId: Long): MyDto? {
    log.debug("Retrieving My: {}", MyId)
    return MyRepo.findOne(MyId)?.toDto()
}

override fun findAllMys(): List<MyDto> {
    log.debug("Retrieving Mys")
    return MyRepo.findAll().map { it.toDto() }
}

override fun updateMy(id: Long?, My: UpdateMyDto): MyDto? {
    log.debug("Updating My: {} with data: {}", id, My)
    val currentMy = MyRepo.findOne(id)
    return if (currentMy != null) MyRepo.save(MyEntity.fromDto(My, currentMy)).toDto()
    else null
}

override fun addMy(My: CreateMyDto): MyDto {
    log.debug("Adding My: {}", My)
    return MyRepo.save(MyEntity.fromDto(My)).toDto()
}

override fun deleteMy(id: Long?) {
    log.debug("Deleting My: {}", id)
    MyRepo.delete(id)
}

冒犯的方法

@Test
fun `'findMyById' should map existing entity from repository`() {
    repository.save(MyEntity(1, "name", "description"))
    val result = service.findByMyId(1)
    softly.assertThat(result?.id).isEqualTo(1)
    softly.assertThat(result?.name).isEqualTo("name")
    softly.assertThat(result?.description).isEqualTo("description")
}

Test failure

org.junit.ComparisonFailure: 
Expected :"name"
Actual   :null

将失败的 findByMyId 方法的名称更改为 getByMyIdretrieveByMyId 允许测试用例从命令行和 IDE 成功通过。无论名称如何,如果作为单个测试运行,该测试将始终从 IDE 运行,但是当测试类作为一个整体运行时,它将失败。

我想知道使用 findByXXId 返回和 Entity 的问题是什么,当我将测试方法的名称更改为以 get 或 retrieve 开头时,这有效。如果我使用任何其他方法名称,它也会失败,更重要的是,即使我在其他服务和测试类中更改方法名称,我也会看到失败,因为 NPE。

如果这没有意义,请提前道歉,但我是这个堆栈的新手,并且在应用程序运行良好的情况下,我花了三天时间来确定为什么这些测试会失败。

【问题讨论】:

  • 听起来您的测试依赖于其他测试,并且名称更改会影响测试的顺序。尝试在 repository.save 之后添加一个 repository.flush 并使用 @Transactional 注释测试
  • 我尝试了这两个建议,但问题仍然存在。如果我将测试方法命名为 findByXXXXId,它会失败,但如果我保持实现完全相同,但将其重命名为 getByXXXXId 或 retrieveByXXXXId,则在执行整个测试类时测试成功通过。

标签: java hibernate junit spring-data-jpa junit5


【解决方案1】:

由于我在 cmets 中的第一个建议似乎没有解决它,这里列出了为了隔离问题而要做的事情。

首先只是为了确保我得到了正确的事实:

  • 您的测试在 IDE 中运行
  • 与所有其他测试(可能使用 Maven 或类似工具)一起运行时,您的测试失败
  • 您的测试在与所有其他测试一起运行时有效,但已重命名。

我对 cme​​ts 的预感仍然成立:这与名称没有直接关系,而是与测试之间的相互依赖关系。

  1. 创建一个在 IDE 中重现问题的最小场景。

    a) 在 IDE 中运行所有测试(应该可以通过选择测试源文件夹并选择“运行测试”或其他方式。

    b) 假设测试失败,通过选择树中越来越小的部分来缩小测试范围。

    c) 如果测试在 IDE 中完全没有失败,您可以使用 Maven 中的包含/排除或任何其他可敬的构建工具来执行相同的操作。

    d) 另一种变化是创建一个包含所有测试的专用测试套件。

    一般来说,通过在每个步骤中删除大约一半的测试,您应该能够在合理的时间内得到一个包含两个测试的测试套件:有问题的测试和另一个触发第一个失败的测试。

  2. 为 SQL 和事务处理激活日志记录。

    您应该会在第一次测试后看到回滚。其次是插入的第二个测试。你不应该看到任何提交。

    如果您没有看到回滚,您的测试要么没有使用 @Transactional 注释,要么由于某种原因没有提取事务。

    如果您没有看到插入,您的更改似乎没有被刷新。

  3. 使用 JDBC 模板发出选择语句来查看数据库的内容。使用像select * from x 这样的简单语句。没有 where 子句,没有连接。记录结果。

有了这些信息,问题对您来说就很明显了。如果不更新问题并对此答案发表评论。我再看看。

【讨论】:

  • 谢谢,我会尽快回复您。
  • 您最初的预感是正确的,我将问题追溯到无法预测的 Junit 测试顺序和 ID 使用 GenerationType.AUTO。每次测试后事务都会回滚,但是用于保存每个新实体的 ID 都会增加,因此无法预测。我将更改测试用例以利用 @BeforeAll 来设置测试数据,而不是在每个测试中都这样做。感谢您的帮助。
  • 另外,更改方法名称有时会起作用,因为它会更改 findBy 方法的顺序,因此它确实会找到 id 为 1 的实体。它将成为第一个运行的方法,并且存储一个实体。
猜你喜欢
  • 2019-10-06
  • 2019-07-05
  • 2021-03-15
  • 2018-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-28
  • 2012-12-13
相关资源
最近更新 更多