英文:
Use PollingConditions with return value in Spock?
问题
我想使用PollingConditions.eventually
来评估并返回值:
@Shared
static PollingConditions POLLING_CONDITIONS = new PollingConditions(
timeout: 30, delay: 1.5, factor: 1.25
)
MyResponseContainerDto sendRequestWithRetries(String body, String uri) {
ExtractableResponse<Response> extractedResponse = null
POLLING_CONDITIONS.eventually {
extractedResponse = sendRequest(body, uri).extract()
assert extractedResponse.statusCode() == 200
}
return extractedResponse.as(MyResponseContainerDto.class)
}
ValidatableResponse sendRequest(String body, String uri) {
return RestAssured.given()
.contentType(JSON)
.body(body)
.when()
.post("/myApi" + "/" + uri)
.then()
.log().all()
}
英文:
I would like to use PollingConditions.eventually
to evaluate and return value:
@Shared
static PollingConditions POLLING_CONDITIONS = new PollingConditions(
timeout: 30, delay: 1.5, factor: 1.25
)
MyResponseContainerDto sendRequestWithRetries(String body, String uri) {
ExtractableResponse<Response> extractedResponse = null
POLLING_CONDITIONS.eventually {
extractedResponse = sendRequest(body, uri).extract()
assert extractedResponse.statusCode() == 200
}
return extractedResponse.as(MyResponseContainerDto.class)
}
ValidatableResponse sendRequest(String body, String uri) {
return RestAssured.given()
.contentType(JSON)
.body(body)
.when()
.post("/myApi" + "/" + uri)
.then()
.log().all()
}
When I try to run the above code I get:
Expected a condition, but found an assignment. Did you intend to write '==' ? @ line 42, column 12.
extractedResponse = sendRequest(body, uri).extract()
^
Is there a possibility to have an assignment inside the eventually
block?
答案1
得分: 2
作为对Marcin建议的替代方案,一个简单的解决方法是使用显式交互块:
package de.scrum_master.stackoverflow.q75663270
import spock.lang.Shared
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
class PollingConditionsTest extends Specification {
@Shared
PollingConditions POLLING_CONDITIONS = new PollingConditions(
timeout : 3, delay : 0.2, factor : 1
)
def myHelper() {
def extractedResponse = null
POLLING_CONDITIONS.eventually {
interaction {
extractedResponse = "xxx"
assert extractedResponse.contains("x")
}
}
extractedResponse
}
def test() {
expect:
myHelper() == "xxx"
}
}
但请注意,因为在你的情况下交互块不在then/expect
中,而是在一个辅助方法中,你仍然需要明确的assert
。
在Groovy Web Console中尝试它。
附言:@Shared
字段不应该是静态的。它是Spock在Spock规范生命周期方面更好的替代方案。
英文:
As an alternative to Marcin's suggestion, a simple workaround would be an explicit interaction block:
package de.scrum_master.stackoverflow.q75663270
import spock.lang.Shared
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
class PollingConditionsTest extends Specification {
@Shared
PollingConditions POLLING_CONDITIONS = new PollingConditions(
timeout : 3, delay : 0.2, factor : 1
)
def myHelper() {
def extractedResponse = null
POLLING_CONDITIONS.eventually {
interaction {
extractedResponse = "xxx"
assert extractedResponse.contains("x")
}
}
extractedResponse
}
def test() {
expect:
myHelper() == "xxx"
}
}
Please note however that, because the interaction block in your case is not inside then/expect
but rather in a helper method, you still need the explicit assert
.
Try it in the Groovy Web Console.
P.S.: A @Shared
field should not be static. It is Spock's better (with regard to Spock spec lifecycle) alternative to static fields.
答案2
得分: 1
引用Spock维护者(来自2018年,但似乎仍然有效):
(...) 在具有隐式断言的块中,除了对新变量的赋值之外,对于防止忘记一个=进行相等检查的常见错误的表达目的,赋值是禁止的,这将不会更改。
PollingConditions
中的eventually
(和within
)方法被标记为@ConditionBlock
,并与then:
和except:
块以相同的方式处理。
作为一种(不太可读的)解决方法,您可以将赋值替换为一些"容器"。在这里,使用AtomicReference
(在JDK中可用)的示例,但任何"自定义容器"都足够:
@Shared
PollingConditions POLLING_CONDITIONS = new PollingConditions(
timeout: 30, delay: 1.5, factor: 1.25
)
MyResponseContainerDto sendRequestWithRetries(String body, String uri) {
AtomicReference<ExtractableResponse<Response>> extractedResponseContainer =
new AtomicReference<>()
POLLING_CONDITIONS.eventually {
extractedResponse.set(sendRequest(body, uri).extract())
assert extractedResponseContainer.get().statusCode() == 200
}
return extractedResponseContainer.get().as(MyResponseContainerDto.class)
}
ValidatableResponse sendRequest(String body, String uri) {
return RestAssured.given()
.contentType(JSON)
.body(body)
.when()
.post("/myApi" + "/" + uri)
.then()
.log().all()
}
或者,您可以使用Awaitility,它在这个领域不那么严格,可以执行"主动等待"。例如,使用该答案中提出的方法。
英文:
Quoting the Spock lead maintainer (from 2018, but it seems to still hold):
> (...) assignments (other than to a new variable) in blocks with implicit assertions are forbidden for the expressed purpose of preventing the common mistake of forgetting one = for an equality check, and this will not be changed.
The eventually
(and within
) methods in PollingConditions
are marked as @ConditionBlock
and treated the same way as the then:
and except:
blocks.
As a (not very readable) workaround, you might replace the assignment with some "container". Here, an example with AtomicReference
(available in JDK), but any "custom container" will be enough:
@Shared
PollingConditions POLLING_CONDITIONS = new PollingConditions(
timeout: 30, delay: 1.5, factor: 1.25
)
MyResponseContainerDto sendRequestWithRetries(String body, String uri) {
AtomicReference<ExtractableResponse<Response>> extractedResponseContainer =
new AtomicReference<>()
POLLING_CONDITIONS.eventually {
extractedResponse.set(sendRequest(body, uri).extract())
assert extractedResponseContainer.get().statusCode() == 200
}
return extractedResponseContainer.get().as(MyResponseContainerDto.class)
}
ValidatableResponse sendRequest(String body, String uri) {
return RestAssured.given()
.contentType(JSON)
.body(body)
.when()
.post("/myApi" + "/" + uri)
.then()
.log().all()
}
Alternatively, you might use Awaitility - which is less strict in that field - to perform "active waiting". For example with the approach proposed in that answer.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论