Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Stranger6667 authored and svtkachenko committed Sep 16, 2019
1 parent f56c7d4 commit 9db8052
Show file tree
Hide file tree
Showing 26 changed files with 279 additions and 343 deletions.
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ look like this:
# test_api.py
import pytest
import requests
from schemathesis import Parametrizer
import schemathesis
BASE_URL = "http://0.0.0.0:8080"
schema = Parametrizer.from_uri(f"{BASE_URL}/swagger.json")
schema = schemathesis.from_uri(f"{BASE_URL}/swagger.json")
@schema.parametrize()
def test_no_server_errors(case):
Expand All @@ -74,7 +74,7 @@ look like this:
It consists of four main parts:

1. Schema preparation; ``Parametrizer`` should be initialized with a valid schema location.
1. Schema preparation; ``schemathesis`` package provides multiple ways to initialize the schema - ``from_path``, ``from_dict``, ``from_uri``, ``from_file``.

2. Test parametrization; ``@schema.parametrize()`` generates separate tests for all endpoint/method combination available in the schema.

Expand Down
2 changes: 1 addition & 1 deletion adr/0002-resolve-schema-references-lazily.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Date: 2019-08-08

## Status

Accepted
Superseded by #6

## Context

Expand Down
2 changes: 1 addition & 1 deletion adr/0003-delay-schema-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Date: 2019-08-08

## Status

Accepted
Superseded by #6

## Context

Expand Down
21 changes: 21 additions & 0 deletions adr/0006-resolve-references-load-schemas-eagerly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 6. Resolve references & load schemas eagerly

Date: 2019-09-15

## Status

Accepted

## Context

Having lazily loaded schema complicates implementation and doesn't bring much value, since loading still happen during the collection phase.
References are not implemented lazily at the moment, it was missed during development.

## Decision

Evaluate schemas & references eagerly.

## Consequences

Implementation will be simpler. However, references still could be implemented lazily,
since they could be evaluated after collection phase and the tests using them could be excluded from the run.
14 changes: 12 additions & 2 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@ Changelog
`Unreleased`_
-------------

Add
~~~
Added
~~~~~

- Generating explicit examples from schema. `#17`_

Changed
~~~~~~~

- Schemas are loaded eagerly from now on. Using ``schemathesis.from_uri`` implies network calls.

Deprecated
~~~~~~~~~~

- Using ``Parametrizer.from_{path,uri}`` is deprecated, use ``schemathesis.from_{path,uri}`` instead

`0.4.1`_ - 2019-09-11
---------------------

Expand Down
16 changes: 0 additions & 16 deletions docs/sources/schemathesis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,6 @@ schemathesis.generator module
:undoc-members:
:show-inheritance:

schemathesis.parametrizer module
--------------------------------

.. automodule:: schemathesis.parametrizer
:members:
:undoc-members:
:show-inheritance:

schemathesis.readers module
---------------------------

.. automodule:: schemathesis.readers
:members:
:undoc-members:
:show-inheritance:

schemathesis.schemas module
---------------------------

Expand Down
7 changes: 2 additions & 5 deletions src/schemathesis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
from .generator import Case
from .parametrizer import Parametrizer
from .schemas import SwaggerV20

# TODO. optional schema validation?
from .models import Case
from .readers import Parametrizer, from_dict, from_file, from_path, from_uri
27 changes: 4 additions & 23 deletions src/schemathesis/generator.py → src/schemathesis/_hypothesis.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,17 @@
"""Provide strategies for given endpoint(s) definition."""
from typing import Callable, Generator

import attr
import hypothesis.strategies as st
from hypothesis import example, given
from hypothesis._strategies import just
from hypothesis_jsonschema import from_schema

from .schemas import Endpoint
from .types import Body, Headers, PathParameters, Query
from .models import Case, Endpoint

PARAMETERS = {"path_parameters", "headers", "query", "body"}
PARAMETERS = frozenset(("path_parameters", "headers", "query", "body"))


@attr.s(slots=True)
class Case:
"""A single test case parameters."""

path: str = attr.ib()
method: str = attr.ib()
path_parameters: PathParameters = attr.ib()
headers: Headers = attr.ib()
query: Query = attr.ib()
body: Body = attr.ib()

@property
def formatted_path(self) -> str:
# pylint: disable=not-a-mapping
return self.path.format(**self.path_parameters)


def create_hypothesis_test(endpoint: Endpoint, test: Callable) -> Callable:
def create_test(endpoint: Endpoint, test: Callable) -> Callable:
"""Create a Hypothesis test."""
strategy = get_case_strategy(endpoint)
wrapped_test = given(case=strategy)(test)
Expand All @@ -47,7 +28,7 @@ def get_examples(endpoint: Endpoint) -> Generator[Case, None, None]:
path=st.just(endpoint.path),
method=st.just(endpoint.method),
**{name: just(parameter["example"])},
**other_parameters
**other_parameters,
).example()


Expand Down
12 changes: 6 additions & 6 deletions src/schemathesis/extra/pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
from _pytest.python import Function, PyCollector # type: ignore
from hypothesis.errors import InvalidArgument # pylint: disable=ungrouped-imports

from ..generator import create_hypothesis_test
from ..parametrizer import is_schemathesis_test
from ..schemas import Endpoint
from .._hypothesis import create_test
from ..models import Endpoint
from ..utils import is_schemathesis_test


def pytest_pycollect_makeitem(collector: nodes.Collector, name: str, obj: Any) -> Optional["SchemathesisCase"]:
"""Switch to a different collector if the test is wrapped with `Parametrizer.parametrize()`."""
"""Switch to a different collector if the test is parametrized marked by schemathesis."""
if is_schemathesis_test(obj):
return SchemathesisCase(obj, name, collector)
return None
Expand All @@ -21,7 +21,7 @@ def pytest_pycollect_makeitem(collector: nodes.Collector, name: str, obj: Any) -
class SchemathesisCase(PyCollector):
def __init__(self, test_function: Callable, *args: Any, **kwargs: Any) -> None:
self.test_function = test_function
self.schemathesis_case = test_function._schema_parametrizer # type: ignore
self.schemathesis_case = test_function._schemathesis_test # type: ignore
super().__init__(*args, **kwargs)

def _get_test_name(self, endpoint: Endpoint) -> str:
Expand All @@ -33,7 +33,7 @@ def _gen_items(self, endpoint: Endpoint) -> Generator[Function, None, None]:
Could produce more than one test item if
parametrization is applied via ``pytest.mark.parametrize`` or ``pytest_generate_tests``.
"""
hypothesis_item = create_hypothesis_test(endpoint, self.test_function)
hypothesis_item = create_test(endpoint, self.test_function)
items = self.ihook.pytest_pycollect_makeitem(
collector=self.parent, name=self._get_test_name(endpoint), obj=hypothesis_item
)
Expand Down
23 changes: 23 additions & 0 deletions src/schemathesis/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import re
from typing import Optional

from .types import Filter
from .utils import force_tuple


def should_skip_method(method: str, pattern: Optional[Filter]) -> bool:
if pattern is None:
return False
patterns = force_tuple(pattern)
return method.upper() not in map(str.upper, patterns)


def should_skip_endpoint(endpoint: str, pattern: Optional[Filter]) -> bool:
if pattern is None:
return False
patterns = force_tuple(pattern)
return not any(is_match(endpoint, item) for item in patterns)


def is_match(endpoint: str, pattern: str) -> bool:
return pattern in endpoint or bool(re.search(pattern, endpoint))
38 changes: 38 additions & 0 deletions src/schemathesis/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from typing import Any, Dict

import attr

from .types import Body, Headers, PathParameters, Query


@attr.s(slots=True)
class Case:
"""A single test case parameters."""

path: str = attr.ib()
method: str = attr.ib()
path_parameters: PathParameters = attr.ib()
headers: Headers = attr.ib()
query: Query = attr.ib()
body: Body = attr.ib()

@property
def formatted_path(self) -> str:
# pylint: disable=not-a-mapping
return self.path.format(**self.path_parameters)


def empty_object() -> Dict[str, Any]:
return {"properties": {}, "additionalProperties": False, "type": "object", "required": []}


@attr.s(slots=True)
class Endpoint:
"""A container that could be used for test cases generation."""

path: str = attr.ib()
method: str = attr.ib()
path_parameters: PathParameters = attr.ib(factory=empty_object)
headers: Headers = attr.ib(factory=empty_object)
query: Query = attr.ib(factory=empty_object)
body: Body = attr.ib(factory=empty_object)
101 changes: 0 additions & 101 deletions src/schemathesis/parametrizer.py

This file was deleted.

0 comments on commit 9db8052

Please sign in to comment.