在Tavern中,如何加载一个公共阶段?

时间:2018-07-02 23:31:26

标签: yaml pyyaml tavern

我正在使用https://github.com/taverntesting/tavern,但以下内容可能确实是一个PyYAML问题。

我们想要一个测试目录,其中每个文件都与API端点匹配。

api/v1/test_thing1.tavern.yaml
api/v1/test_thing2.tavern.yaml

,依此类推。每个YAML文档都需要一个login,它可以放在树顶部的common_stages.yaml中。我尝试过的大多数事情都以PyYAML出现此错误而告终

yaml.scanner.ScannerError: mapping values are not allowed here

common_stages.yaml看起来像这样:

---
stages:
- name: &login_required
  request:
    url: "{host}/api/v1/login"
    json:
      username: "{username}"
      password: "{password}"
    method: POST
    headers:
      content-type: application/json
  response:
    status_code: 201
    cookies:
      - session
    headers:
      content-type: application/json

一个测试文件如下:

---
test_name: Get thing1 list

includes:
  - !include ../../common.yaml

stages:
  - name: Get category list
    request:
      url: "{host}/api/v1/thing1"
      method: GET
    response:
      status_code: 200
      headers:
        content-type: application/json

我尝试将common_stages包含添加到具有common的列表中。我尝试将其包含在stages的{​​{1}}行中。不高兴。

所有的Tavern示例将YAML文档显示为一个长文件。这对于演示很好,但在现实世界中并不适用。

2 个答案:

答案 0 :(得分:0)

我为小酒馆使用的加载程序做了一个稍微简化的版本(因为您的文件中没有任何别名,因此不需要跨文件锚点支持)。

如果您!include正确的文件,文件加载正常,没有任何错误。在您的问题中,您引用common_stages.yaml,但在您的test_thing1.tavern.yaml中,您引用!include ../../common.yaml。因此,您的common.yaml最有语法错误,而您在此处出现的common_stages.yaml没有语法错误。

证明文件可以正常加载

import os
from pathlib import Path
import yaml

from yaml.reader import Reader
from yaml.scanner import Scanner
from yaml.parser import Parser
from yaml.composer import Composer
from yaml.constructor import SafeConstructor
from yaml.resolver import Resolver

# this class slightly simplified from 
# https://github.com/taverntesting/tavern/blob/master/tavern/util/loader.py
class IncludeLoader(Reader, Scanner, Parser, Composer, Resolver,
        SafeConstructor):
    """YAML Loader with `!include` constructor and which can remember anchors
    between documents"""

    def __init__(self, stream):
        """Initialise Loader."""

        # pylint: disable=non-parent-init-called

        try:
            self._root = os.path.split(stream.name)[0]
        except AttributeError:
            self._root = os.path.curdir
        self.anchors = {}
        Reader.__init__(self, stream)
        Scanner.__init__(self)
        Parser.__init__(self)
        SafeConstructor.__init__(self)
        Resolver.__init__(self)


def construct_include(loader, node):
    """Include file referenced at node."""

    # pylint: disable=protected-access
    filename = os.path.abspath(os.path.join(
        loader._root, loader.construct_scalar(node)
    ))
    extension = os.path.splitext(filename)[1].lstrip('.')

    if extension not in ('yaml', 'yml'):
        raise BadSchemaError("Unknown filetype '{}'".format(filename))

    with open(filename, 'r') as f:
        return yaml.load(f, IncludeLoader)


IncludeLoader.add_constructor("!include", construct_include)

Path('common.yaml').write_text("""\
---
stages:
- name: &login_required
  request:
    url: "{host}/api/v1/login"
    json:
      username: "{username}"
      password: "{password}"
    method: POST
    headers:
      content-type: application/json
  response:
    status_code: 201
    cookies:
      - session
    headers:
      content-type: application/json
""")

t_yaml = 'test_thing1.tavern.yaml'
Path(t_yaml).write_text("""\
---
test_name: Get thing1 list

includes:
  - !include common.yaml

stages:
  - name: Get category list
    request:
      url: "{host}/api/v1/thing1"
      method: GET
    response:
      status_code: 200
      headers:
        content-type: application/json
""")


data = yaml.load(open(t_yaml), Loader=IncludeLoader)

print(data['includes'][0]['stages'][0]['request']['url'])

输出:

{host}/api/v1/login

答案 1 :(得分:0)

对于那些有同样问题的人。

您可以使用 TAVERN_INCLUDE 环境变量。

在其中,您必须指定 Tavern 将在其中搜索“include”中指定的文件的文件夹的路径。 路径必须是绝对路径。

示例:

// using namespace myspace;
int main(int argc, char** argv){
   extern class myclass   c(); // this makes linker look for "myclass"
   c.foo();
}

默认情况下,它会在与测试相同的文件夹中查找文件。 在“包含”中,您还可以指定子文件夹中的文件。

示例:

TAVERN_TESTS_HOME = /tests/home
TAVERN_INCLUDE = $TAVERN_TESTS_HOME/common

文档: https://tavern.readthedocs.io/en/latest/basics.html#including-external-files