2017-11-10 109 views
0

我正在使用SpringBoot/Kotlin/JPA/Hibernate/Junit并且有JpaServiceTest类,它可以执行属于单个实体的存储库方法。 JpaService Class的方法名称遵循约定findByXXXXId,findAll,updateXXXX,addXXXXdeleteXXXXDataJPATest Junit方法命名和NullPointerExceptions

为了保持一致性,我使用相同的约定为JpaTest类中的方法命名。我的JpaTest类有两个findById场景,其中'Null'预期是另一个映射实体返回的地方。我的应用程序按预期工作,但是我的测试类在预期返回有效实体的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返回时的问题和实体,当我更改测试方法的名称以开始获取或检索时,这会起作用。如果我使用任何其他方法名称,它也会失败,甚至当我在其他服务和测试类中更改方法名称时,由于NPE,我看到失败。

如果这没有意义,我会事先道歉,但我是这个堆栈的新手,需要三天的时间才能确定这些测试在应用程序完美运行时失败的原因。

+1

听起来像你的测试取决于其他测试,名称的变化影响测试的顺序。尝试在repository.save之后添加一个repository.flush并使用@Transactional注释测试 –

+0

我尝试了这两个建议,但问题仍然存在。如果我将测试方法命名为findByXXXXId,它会失败,但如果我保持实现完全相同,并将其重命名为getByXXXXId或retrieveByXXXXId,则在执行整个测试类时测试会成功通过。 – hoos

回答

1

由于我在评论中的第一条建议似乎没有解决它,因此这里列出了一些要解决问题的方法。

首先只是为了确保我已经得到了澄清事实:

  • 您的测试工作在IDE的时候与其他所有的测试运行(假设使用Maven或相似)
  • 你的测试失败
  • 您的测试在与所有其他测试一起运行时工作,但已重命名。

我对这个评论的预感仍然存在:这与名字没有直接关系,而是与测试之间的相互依赖关系。

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

    一个)运行IDE中的所有测试(应该通过选择测试源文件夹并选择“运行测试”或什么是可能的。

    b)中假设测试失败,通过选择较小的减少测试范围和树的较小部分。 c)如果测试在IDE中完全没有失败,那么可以使用Maven中的include/exclude或其他可以使用的构建工具来做同样的事情。

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

    通常,通过在每个步骤中删除大约一半的测试,您应该能够在合理的时间内完成两个测试的测试套件:有问题的测试和另外一个测试触发第一个测试失败。

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

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

    如果您没有看到回滚,那么您的测试或者没有用@Transactional进行注释,或者由于某种原因没有收到交易。

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

  3. 使用JDBC模板问题select语句查看数据库的内容。使用简单的陈述,如select * from x。没有where子句,没有连接。记录结果。

很有可能是这个信息,这个问题对你来说变得很明显。如果不更新这个问题并评论这个答案。我会再看一次。

+0

谢谢,我会尽快回复您。 – hoos

+0

您的初始预感是正确的,我将问题追溯到Junit测试不可预测的顺序,ID使用GenerationType.AUTO。交易在每次测试后回滚,但用于保存每个新实体的ID增加并且因此不可预测。我将改变测试用例来利用@BeforeAll来设置测试数据,而不是在每个测试中都进行测试。感谢帮助。 – hoos

+0

另外,更改方法名称有时候会起作用,因为它会改变findBy方法的顺序,因此它确实会找到id为1的实体。它将成为运行和存储实体的第一个方法。 – hoos