使用Spock中带有返回值的PollingConditions吗?

huangapple go评论177阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年3月7日 22:28:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75663270.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定