为什么我的测试在拆分 mocking 和 stubing 时不起作用?

Why does my test not work when splitting mocking and stubbing?

提问人:michalbarnat 提问时间:11/12/2023 最后编辑:kriegaexmichalbarnat 更新时间:11/14/2023 访问量:55

问:

我在 Spock/Groovy 测试中苦苦挣扎。 不知道为什么,但在WHEN部分,我无法检查调用次数并立即检查值。 如果我单独检查其中一个,一切正常!

此测试通过:

def "should throw AppointmentNotFoundException when appointment do not exist" () {
  given:
  long nonExistingAppointmentId = 69L

  when:
  appointmentRepository.findById(nonExistingAppointmentId) >> { throw new AppointmentNotFoundException("Appointment with id " + nonExistingAppointmentId + " not found") }
  findAppointmentQueryHandler.handle(nonExistingAppointmentId)

  then:
  thrown(AppointmentNotFoundException)
}

而且这个通过没有任何问题:

def "should throw AppointmentNotFoundException when appointment do not exist" () {
  given:
  long nonExistingAppointmentId = 69L

  when:
  appointmentRepository.findById(nonExistingAppointmentId) >> { throw new AppointmentNotFoundException("Appointment with id " + nonExistingAppointmentId + " not found") }
  findAppointmentQueryHandler.handle(nonExistingAppointmentId)

  then:
  1 * appointmentRepository.findById(69L)
}

但是,如果我尝试进行 1 次测试来检查所有测试,那么我就有错误了

def "should throw AppointmentNotFoundException when appointment do not exist" () {
  given:
  long nonExistingAppointmentId = 69L

  when:
  appointmentRepository.findById(nonExistingAppointmentId) >> { throw new AppointmentNotFoundException("Appointment with id " + nonExistingAppointmentId + " not found") }
  findAppointmentQueryHandler.handle(nonExistingAppointmentId)

  then:
  1 * appointmentRepository.findById(69L)
  thrown(AppointmentNotFoundException)
}

错误:

Expected exception of type 'com.bbc.anotherhospital.exceptions.AppointmentNotFoundException', but no exception was thrown
    at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
    at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
    at com.bbc.anotherhospital.appointment.handlers.FindAppointmentQueryHandlerSpec.should throw AppointmentNotFoundException when appointment do not exist

首先,我认为我的代码或代码有问题。但是应用程序运行良好,在 Postman 中进行了测试。

有人知道它为什么会这样吗?

我的pom:

<dependency>
  <groupId>org.apache.groovy</groupId>
  <artifactId>groovy-all</artifactId>
  <version>4.0.15</version>
  <type>pom</type>
</dependency>
<dependency>
  <groupId>org.spockframework</groupId>
  <artifactId>spock-core</artifactId>
  <version>2.4-M1-groovy-4.0</version>
  <scope>test</scope>
</dependency>
Java 单元测试 测试 Groovy Spock

评论


答:

2赞 kriegaex 11/13/2023 #1

这是 Spock 的常见问题解答,也在 Stack Overflow 上。Spock 手册清楚地解释了为什么你正在做的事情不起作用:

结合嘲笑和存根

嘲笑和存根是相辅相成的:

1 * subscriber.receive("message1") >> "ok"
1 * subscriber.receive("message2") >> "fail"

当模拟和存根相同的方法调用时,它们必须在同一交互中发生。特别是,以下 Mockito 样式的存根和 mocking 拆分为两个单独的语句将不起作用

given:
subscriber.receive("message1") >> "ok"

when:
publisher.send("message1")

then:
1 * subscriber.receive("message1")

如在何处声明交互中所述,调用将首先与块中的交互进行匹配。由于该交互未指定响应,因此将返回方法返回类型(在本例中为)的默认值。(这只是斯波克宽容嘲讽的另一个方面。因此,块中的交互将永远没有机会匹配。receivethen:nullgiven:

注意:同一方法调用的模拟和存根必须在同一交互中发生。

也就是说,你想使用类似的东西

1 * appointmentRepository.findById(nonExistingAppointmentId) >> {
  throw new AppointmentNotFoundException("Appointment with id " + nonExistingAppointmentId + " not found")
}

在你的街区。then:

评论

0赞 kriegaex 11/15/2023
其实,现在我有点生气。我的答案是正确的,首先在这里,甚至解释了为什么它没有按照您尝试的方式工作。昨天,我在提交 9765c936 时克隆了您的存储库,并完全按照上述方式更改了 FindAppointmentQueryHandlerSpec 中的此部分。它解决了这个问题。你为什么用相同的解决方案接受后来的答案?
0赞 kriegaex 11/15/2023
这是我的解决方案在您的存储库中有效的证据:更改的代码测试结果
0赞 tim_yates 11/14/2023 #2

我必须添加一些东西,以便我可以运行您的示例,但是:

class TestSpec extends Specification {

    def "should throw AppointmentNotFoundException when appointment do not exist" () {
        given:
        AppointmentRepository appointmentRepository = Mock()
        FindAppointmentQueryHandler findAppointmentQueryHandler = new FindAppointmentQueryHandler(appRepository: appointmentRepository)
        long nonExistingAppointmentId = 69L

        when:
        findAppointmentQueryHandler.handle(nonExistingAppointmentId)

        then:
        def ex = thrown(AppointmentNotFoundException)
        ex.message == "Appointment with id $nonExistingAppointmentId not found"

        1 * appointmentRepository.findById(69L) >> {
            throw new AppointmentNotFoundException("Appointment with id " + nonExistingAppointmentId + " not found")
        }
    }

    static class FindAppointmentQueryHandler {

        AppointmentRepository appRepository

        String handle(long id) {
            appRepository.findById(id)
        }
    }

    static class AppointmentRepository {

        String findById(long id) {
            "called $id"
        }
    }

    static class AppointmentNotFoundException extends RuntimeException {

        AppointmentNotFoundException(String message) {
            super(message)
        }
    }
}

工作?