Groovy 带剧本的RESTAPI测试:从任务结果中获取一个值,并与其他任务共享

Groovy 带剧本的RESTAPI测试:从任务结果中获取一个值,并与其他任务共享,groovy,rest-assured,serenity-bdd,Groovy,Rest Assured,Serenity Bdd,我正在尝试使用RESTAPI实现登录步骤。为了实现这一点,我需要能够获取从一个任务返回的JWT令牌,并使用它对其他任务进行身份验证。理论上我知道怎么做。请考虑以下Groovy代码。 class ActorInTheSpotlight { String actor String token @Step("{0} is an actor using the system under test") ActorInTheSpotlight whoIsNamed(Stri

我正在尝试使用RESTAPI实现登录步骤。为了实现这一点,我需要能够获取从一个任务返回的JWT令牌,并使用它对其他任务进行身份验证。理论上我知道怎么做。请考虑以下Groovy代码。

class ActorInTheSpotlight {
    String actor
    String token

    @Step("{0} is an actor using the system under test")
    ActorInTheSpotlight whoIsNamed(String actor) {
        this.actor = actor
        theActorCalled(actor)

        return this
    }

    @Step("#actor who can authenticate with credentials")
    ActorInTheSpotlight whoCanAuthenticateWith(String email, String password) {
        theActor().whoCan(Authenticate.withCredentials(email, password))

        return this
    }

    @Step("#actor who can call the admin API")
    ActorInTheSpotlight whoCanCallTheAdminApi() {
        theActor().whoCan(CallAnApi.at("http://localhost:3000"))
        return this
    }

    @Step("#actor was able to login to API with credentials")
    ActorInTheSpotlight wasAbleToLoginToApi() {
        return wasAbleTo(LoginWithApi.usingCredentials())
    }

    ActorInTheSpotlight wasAbleTo(Performable... todos) {
        theActor().wasAbleTo(todos)
        return this
    }
}
问题是,我不确定在步骤定义的上下文中把这段代码放在哪里。我应该将检索此令牌作为自己的步骤来实现,还是有办法将此实现细节隐藏在
LoginToApi
任务本身中

谢谢你的时间

更新

下面是
Authenticate
能力类,它可能是实现此功能的好地方,但上面提到的时间问题仍然适用。也就是说,我将如何更新“飞行中”的能力,以便在正确的时间用于其他任务的消耗

class Authenticate implements Ability {

    String email

    String password

    // instantiates the Ability and enables fluent DSL
    static Authenticate withCredentials(String email, String password) {
        return new Authenticate(email, password)
    }

    // NOTE: custom exception class not shown
    static Authenticate asPrincipal(Actor actor) throws CannotAuthenticateException {
        // complain if someone's asking the impossible
        if(!actor.abilityTo(Authenticate.class)){
            throw new CannotAuthenticateException(actor.getName())
        }

        return actor.abilityTo(Authenticate.class)
    }

    Authenticate(String email, String password) {
        this.email = email
        this.password = password
    }
}
更新2

我能够将其作为自己的步骤来实现,但我真的不喜欢我的实现细节像这样泄漏到步骤定义中。我想知道的是,任何允许我在没有
的情况下实现这一点的答案都是能够获得有效的jwt令牌的

注意:仅显示对原始代码的添加

下面是一个使用JWT令牌对请求进行身份验证的任务示例:

class ApiGet implements Task {

    static ApiGet from(String resource) {
        return instrumented(ApiGet.class, resource)
    }

    String resource

    ApiGet(String resource) {
        this.resource = resource
    }

    @Step("{0} attempts to GET #resource")
    <T extends Actor> void performAs(T actor) {
        actor.attemptsTo(
                // NOTE: GetFromApi is an alias for Get, renaming `with` to `withRequest`
                // so that Groovy does not attempt to match it to the default `with(Closure closure)`
                GetFromApi.at(resource).withRequest({ RequestSpecification req ->
                    AuthenticateApi.attempt(actor, req)
                    req.header("Content-Type", "application/json")
                })
        )
    }
}
类ApiGet实现任务{
静态ApiGet from(字符串资源){
检测返回(ApiGet.class,资源)
}
字符串资源
ApiGet(字符串资源){
this.resource=resource
}
@步骤(“{0}尝试获取#资源”)
无效性能(T系数){
actor.attemptto(
//注意:GetFromApi是Get的别名,将'with'重命名为'withRequest'`
//因此Groovy不会尝试将其与默认的`with(Closure)匹配`
GetFromApi.at(资源).withRequest({RequestSpecification req->
AuthenticateApi.尝试(参与者,请求)
请求标题(“内容类型”、“应用程序/json”)
})
)
}
}

看起来线程不太安全,但这一切都不是真的,所以。。。无聊的。这是我想到的

class AdminApiStepDefinitions {
    @Given(/^s?he was able to login to the api with the credentials$/)
    void was_able_to_login_to_the_api_with_the_credentials(Map<String, String> credentials) {
        def email = credentials.get('email')
        def password = credentials.get('password')
        theActor
                .whoCanAuthenticateWith(email, password)
                .wasAbleToLoginToApi()

        theActor.whoHasTheToken(SerenityRest.lastResponse().jsonPath().getString("token"))
    }
}

类定义{
@给定(/^s?他能够使用凭据$/)登录到api
void可以使用凭据(映射凭据)登录api{
def email=credentials.get('email')
def password=credentials.get('password')
演员
.whoCanAuthenticateWith(电子邮件、密码)
.wasAbleToLoginToApi()
theActor.WhoHasToken(SerenityRest.lastResponse().jsonPath().getString(“令牌”))
}
}
class Authenticate implements Ability {

    String email

    String password

    // instantiates the Ability and enables fluent DSL
    static Authenticate withCredentials(String email, String password) {
        return new Authenticate(email, password)
    }

    // NOTE: custom exception class not shown
    static Authenticate asPrincipal(Actor actor) throws CannotAuthenticateException {
        // complain if someone's asking the impossible
        if(!actor.abilityTo(Authenticate.class)){
            throw new CannotAuthenticateException(actor.getName())
        }

        return actor.abilityTo(Authenticate.class)
    }

    Authenticate(String email, String password) {
        this.email = email
        this.password = password
    }
}
class ActorInTheSpotlight {
    @Step("#actor has a valid JWT token")
    ActorInTheSpotlight whoHasTheToken(String token) {
        this.token = token
        theActor().whoCan(AuthenticateApi.withToken(token))
        return this
    }
}
class AuthenticateApi implements Ability {

    String token

    static AuthenticateApi withToken(String token) {
        return new AuthenticateApi(token)
    }

    static AuthenticateApi asPrincipal(Actor actor) throws CannotAuthenticateException {
        // complain if someone's asking the impossible
        if(!actor.abilityTo(AuthenticateApi.class)){
            throw new CannotAuthenticateException(actor.getName())
        }

        return actor.abilityTo(AuthenticateApi.class)
    }

    static <T extends Actor> void attempt(final T actor, final RequestSpecification req) {
        AuthenticateApi auth = null
        try {
            auth = AuthenticateApi.asPrincipal(actor)
        }
        catch(CannotAuthenticateException e) {
            // swallow error
        }

        if(auth) {
            req.header("Authorization", "Bearer ${auth.token}")
        }
    }

    AuthenticateApi(String token) {
        this.token = token
    }
}
class AdminApiStepDefinitions {
    // This is what I want to get rid of!
    @Given(/^s?he was able to get a valid JWT token$/)
    void was_able_to_get_a_valid_jwt_token() {
        theActor.whoHasTheToken(SerenityRest.lastResponse().jsonPath()
                .getObject("token", String.class))
    }
}
class ApiGet implements Task {

    static ApiGet from(String resource) {
        return instrumented(ApiGet.class, resource)
    }

    String resource

    ApiGet(String resource) {
        this.resource = resource
    }

    @Step("{0} attempts to GET #resource")
    <T extends Actor> void performAs(T actor) {
        actor.attemptsTo(
                // NOTE: GetFromApi is an alias for Get, renaming `with` to `withRequest`
                // so that Groovy does not attempt to match it to the default `with(Closure closure)`
                GetFromApi.at(resource).withRequest({ RequestSpecification req ->
                    AuthenticateApi.attempt(actor, req)
                    req.header("Content-Type", "application/json")
                })
        )
    }
}
class AdminApiStepDefinitions {
    @Given(/^s?he was able to login to the api with the credentials$/)
    void was_able_to_login_to_the_api_with_the_credentials(Map<String, String> credentials) {
        def email = credentials.get('email')
        def password = credentials.get('password')
        theActor
                .whoCanAuthenticateWith(email, password)
                .wasAbleToLoginToApi()

        theActor.whoHasTheToken(SerenityRest.lastResponse().jsonPath().getString("token"))
    }
}