空手道模拟复杂端点的最佳实践 [英] best practices for mocking complex endpoints with karate-netty

查看:30
本文介绍了空手道模拟复杂端点的最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在空手道网络中构建复杂功能的最佳实践/建议是什么?

What are the best practices / recommendations around building complex functionality in karate-netty?

我们需要编写一个相对复杂的模拟服务,以避免碰到我们在较低地区的集成合作伙伴.我们大约有8个端点,包括搜索,查找和其他信息,用于结帐渠道的后续阶段.我们需要做一些事情,例如对于id = 7,返回此有效载荷,但是对于id = 8,则返回该有效载荷.空手道网很好地支持了所有这些!但是,我们的另一个要求是紧密地反映错误行为,例如丢失auth标头,有效载荷的形状错误等.通过这种条件/异常处理,我们努力在空手道网络中找到对我们有用的范例.

We have a requirement to write a relatively complex mock service to avoid hitting our integration partner in lower regions. We have about 8 endpoints comprising search, lookup, and additional information for later stages of our checkout funnel. We need to do things like for id = 7, return this payload, but for id = 8, return that payload. karate-netty supports all of this very nicely! However, an additional requirement we have is to closely mirror error behavior - eg missing auth header, wrong shape of payload, etc. With this sort of conditional/exception handling, we struggled to find a paradigm within karate-netty that worked for us.

要解释我的意思,请想象一个需要auth标头,有效request.foo和有效request.bar的端点.您可能会这样写:

To explain what I mean, imagine an endpoint which requires an auth header, and valid request.foo, and valid request.bar. You might write something like this:

Scenario: pathMatches('ourEndpoint') && methodIs('post') && !headerContains('Auth', 'secret')
  * call read('403.feature')

Scenario: pathMatches('ourEndpoint') && methodIs('post') && !request.foo
  * call read('422.feature') { missing: 'foo' }

Scenario: pathMatches('ourEndpoint') && methodIs('post') && !request.bar
  * call read('422.feature') { missing: 'bar' }

Scenario: pathMatches('ourEndpoint') && methodIs('post') && headerContains('Auth', 'secret') && request.foo && request.bar
  * call read('200.feature')

对于像这样的小型示例来说,这很好,但是对于复杂的端点进行大量验证的这种方法的问题在于,您必须两次指定所有条件-在错误情况下一次指定在200个情况下一次.当您有10个端点的不同方案时,这并不是最佳选择-特别是您的200个条件变得极其笨拙.如果有像karate.abort()这样的东西而不是中止场景而是中止整个功能-karate.abortFeature()(如果愿意),那将是很好的.然后,您只需指定一次错误条件,并简化200种情况的条件,即可简单地处理对之前未出错的端点的每个请求,例如:

This is fine for small examples like this, but the problem with this approach for complicated endpoints doing a lot of validation is that you have to specify all of your conditions twice - once in the error case and once in the 200 case. This is not so optimal when you have 10 different scenarios for an endpoint - your 200 condition in particular becomes extremely unwieldy. It would be nice if there was something like karate.abort() that instead of aborting a Scenario instead aborted an entire feature - karate.abortFeature() if you will. Then you would only have to specify your error conditions once and simplify the condition for the 200 case to simply handle every request to that endpoint that hasn't errored out before, eg:

Scenario: pathMatches('ourEndpoint') && methodIs('post') && !headerContains('Auth', 'secret')
  * call read('403.feature')
  * karate.abortFeature()

Scenario: pathMatches('ourEndpoint') && methodIs('post') && !request.foo
  * call read('422.feature') { missing: 'foo' }
  * karate.abortFeature()

Scenario: pathMatches('ourEndpoint') && methodIs('post') && !request.bar
  * call read('422.feature') { missing: 'bar' }
  * karate.abortFeature()

Scenario: pathMatches('ourEndpoint') && methodIs('post')
  * call read('200.feature')

如果有某种方法可以支持多个级别的过滤,那就太好了;那么我们可以拥有像这样的每个端点的特征文件:

It would also be nice if there were some way to support filtering at multiple levels; then we could have per-endpoint feature files like so:

Feature: pathMatches('ourEndpoint') && methodIs('post')
  Scenario: !headerContains('Auth', 'secret')
    * call read('403.feature')
    * karate.abortFeature()

  Scenario: !request.foo
    * call read('422.feature') { missing: 'foo' }
    * karate.abortFeature()

  Scenario: !request.bar
    * call read('422.feature') { missing: 'bar' }
    * karate.abortFeature()

  Scenario:
    * call read('200.feature')

缺少上述内容,我们将所有条件和异常流都拉到一个场景中,如下所示:

Lacking the above, we went with pulling all the conditionals and exception flows in to a single scenario, something like this:

* def abortWithResponse = 
"""
  function(responseStatus, response) { 
    karate.set('response', response);
    karate.set('responseStatus', responseStatus);
    karate.abort();
  }
"""

Scenario: pathMatches('ourEndpoint') && methodIs('post')
  * if (!headerContains('Auth', 'secret')) abortWithResponse(403, read('403response.json'));
  * if (!request.foo) abortWithResponse(422, build422Response({missing: ['foo']));
  * if (!request.bar) abortWithResponse(422, build422Response({missing: ['bar']));
  * call read('200.feature')

这有效,但是感觉很不雅且难以阅读.任何人都可以使用不同的模式来实现这些目标吗?如果不是,添加karate.abortFeature()是否会是有用的添加?

This works, but feels rather inelegant and hard to read. Is there a different pattern that anyone is using to accomplish these ends? If not, would adding karate.abortFeature() be a useful addition?

提前谢谢!

推荐答案

我实际上认为您的最后一个示例(单个场景)非常优雅:)

I actually thought your last example (single scenario) was pretty elegant :)

首先,我的团队通常做的是相反的-我们的一些模拟游戏很长,因为我们不介意包含一百万个场景,每个场景都有自己的一点点扭曲(仅 此请求的问题是这个特定的标头,因此这是我们针对该问题的非常特殊的响应),主要是因为在Karate中编写服务器端方案是如此容易.

Fwiw, what my team does is typically the opposite -- some of our mocks are quite lengthy, as we don't mind including a million scenarios, each with one little twist of their own (the only thing wrong with this request is this particular header, and therefore here is our very particular response for that issue), mostly because it's so easy to write server-side scenarios in Karate.

但是,我们倾向于利用的一件事是一个背景函数或三个函数来处理请求必须通过才能获得200的所有验证.例如,我们可能需要5或10个见有效载荷.在这种情况下,我们可能会定义一些"payloadValidator"这样的功能:

One thing we do tend to leverage, however, is a Background function or three to take care of ALL the validation a request must pass in order to get a 200. For instance, we might have 5 or 10 things we need to see in the payload. In that case, we'll probably define some "payloadValidator" function as such:

    * def payloadValidator =
      """
        function() {
          return karate.match("request contains{ id: '123', localTime: '045940', localDate: '1216', ref: 'A0A0' }").pass
        }
      """

...然后,我们只需在其中添加"payloadValidator()"在我们200个响应方案的表达式中.然后,下面的所有内容都可以一次专注于有效负载的一个特定问题(在另一个场景中,可以像您所举的例子那样利用IF语句之类的东西,也可以跨多个场景……您选择).

...then, we'd simply include "payloadValidator()" in the expression for our 200 response scenario. Everything underneath can then focus on one particular issue with the payload at a time (either within one more scenario by leveraging things like IF statements, as you exampled, or spread across multiple scenarios...your choice).

这篇关于空手道模拟复杂端点的最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆