diff --git a/rester/__init__.py b/rester/__init__.py
index 59cd959..05a8ba7 100644
--- a/rester/__init__.py
+++ b/rester/__init__.py
@@ -1 +1 @@
-from . import apirunner
\ No newline at end of file
+from . import apirunner
diff --git a/rester/examples/prevoty/test_suite.json b/rester/examples/prevoty/test_suite.json
index 87cb75d..cec1382 100755
--- a/rester/examples/prevoty/test_suite.json
+++ b/rester/examples/prevoty/test_suite.json
@@ -1,3 +1,4 @@
{
+ "py/object":"rester.TestSuite",
"test_cases":["tests/weather.json"]
}
diff --git a/rester/examples/prevoty/tests/xss.json b/rester/examples/prevoty/tests/xss.json
index 68bcebf..3361382 100755
--- a/rester/examples/prevoty/tests/xss.json
+++ b/rester/examples/prevoty/tests/xss.json
@@ -1,52 +1,62 @@
{
- "name":"Prevoty XSS",
- "globals":{
- "variables":{
- "baseApiUrl":"https://api.prevoty.com",
- "api_key":"82ddb7e2-883c-4290-83f7-d15a8ba4aa7e",
- "rule_key":"108ee29a-a2cb-49ec-b053-2e82f198ce08",
- "expected_value":2
- }
- },
- "testSteps":[
- {
- "name":"Prevoty Key Verification",
- "dumpResponse":true,
- "skip":false,
- "apiUrl":"{baseApiUrl}/1/key/verify?api_key={api_key}",
- "assertMap": {
- "payLoad":{
- "verified":true,
- "message":"api_key is valid"
- }
+ "py/object":"rester.loader.TestCase",
+ "py/state": {
+ "name": "Prevoty XSS",
+ "variables": {
+ "py/object": "rester.manifest.Variables",
+ "variables": {
+ "baseApiUrl": "https://api.prevoty.com",
+ "api_key": "82ddb7e2-883c-4290-83f7-d15a8ba4aa7e",
+ "rule_key": "108ee29a-a2cb-49ec-b053-2e82f198ce08",
+ "expected_value": 2
+ }
+ },
+ "testSteps": [
+ {
+ "py/object": "rester.loader.TestStep",
+ "py/state": {
+ "name": "Prevoty Key Verification",
+ "dumpResponse": true,
+ "skip": false,
+ "apiUrl": "{baseApiUrl}/1/key/verify?api_key={api_key}",
+ "assertMap": {
+ "payLoad": {
+ "verified": true,
+ "message": "api_key is valid"
+ }
+ }
}
- },
- {
- "name":"XSS Basic test",
- "apiUrl":"{baseApiUrl}/1/xss/filter",
- "headers":{
- "content-type":"application/json;"
- },
- "method":"post",
- "params":{
- "api_key":"{api_key}",
- "input":"",
- "rule_key":"{rule_key}"
- },
- "assertMap":{
- "headers":{
- "content-type":"application/json; charset=utf-8"
- },
- "payLoad":{
- "message":"",
- "output":"Hello World!",
- "output":"String",
- "statistics.javascript_tags":"-ge {expected_value}",
- "statistics.transformations":"int",
- "statistics.transformations":"-gt 3",
- "statistics.prevoty_link_metadata":"Object"
- }
+ },
+ {
+ "py/object": "rester.loader.TestStep",
+ "py/state": {
+ "name": "XSS Basic test",
+ "apiUrl": "{baseApiUrl}/1/xss/filter",
+ "headers": {
+ "content-type": "application/json;"
+ },
+ "method": "post",
+ "params": {
+ "api_key": "{api_key}",
+ "input": "",
+ "rule_key": "{rule_key}"
+ },
+ "assertMap": {
+ "headers": {
+ "content-type": "application/json; charset=utf-8"
+ },
+ "payLoad": {
+ "message": "",
+ "output": "Hello World!",
+ "output": "String",
+ "statistics.javascript_tags": "-ge {expected_value}",
+ "statistics.transformations": "int",
+ "statistics.transformations": "-gt 3",
+ "statistics.prevoty_link_metadata": "Object"
+ }
+ }
}
- }
- ]
+ }
+ ]
+ }
}
diff --git a/rester/exc.py b/rester/exc.py
index 5f3c7d7..e69bd0d 100644
--- a/rester/exc.py
+++ b/rester/exc.py
@@ -26,7 +26,7 @@ def __call__(self):
for step in self.case.steps:
self.logger.debug('Test Step Name : %s', step.name)
- if step.get('skip', False):
+ if step.skip:
self.logger.info('\n=======> Skipping test case : ' + step.name)
self.skipped.append(step)
continue
@@ -61,7 +61,7 @@ def _format_logs(self, lc):
def _build_param_dict(self, test_step):
params = {}
if hasattr(test_step, 'params') and test_step.params is not None:
- for key, value in test_step.params.items().items():
+ for key, value in test_step.params.iteritems():
params[key] = self.case.variables.expand(value)
return params
@@ -77,7 +77,7 @@ def _execute_test_step(self, test_step):
headers = {}
if hasattr(test_step, 'headers') and test_step.headers is not None:
self.logger.debug('Found Headers')
- for key, value in test_step.headers.items().items():
+ for key, value in test_step.headers.iteritems():
headers[key] = self.case.variables.expand(value)
# process and set up params
@@ -139,7 +139,7 @@ def _assert_element_list(self, section, failures, test_step, response, assert_li
if value in json_types:
self.logger.info('Found json type : %s ', value)
- if type(json_eval_expr) == DictWrapper:
+ if type(json_eval_expr) == dict:
value = 'Object'
json_eval_expr = {}
diff --git a/rester/loader.py b/rester/loader.py
index 29715a9..2213ad5 100644
--- a/rester/loader.py
+++ b/rester/loader.py
@@ -1,3 +1,5 @@
+import jsonpickle
+
from rester.struct import DictWrapper
import json
import os
@@ -23,38 +25,87 @@ def _load(self, data):
filename = os.path.join(os.path.dirname(self.filename), case)
self.test_cases.append(TestCase(self, filename))
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ # del elements of state you don't want to pickle here
+ return state
+
+ def __setstate__(self, state):
+ """
+ behavior for unpickling. setting default values here
+ :param state:
+ :return:
+ """
+ # Manipulate state here before assigning it to our instance
+
+ self.__dict__.update(state)
+
class TestCase(object):
- def __init__(self, suite, filename):
- self.filename = filename
- self.data = None
+ def __init__(self, suite):
+ """
+ generate a TestCase
+ :param suite: none if not part of a suite.
+ """
self.variables = Variables()
if suite:
self.variables.update(suite.variables)
- self.load()
- def __getattr__(self, key):
- return getattr(self.data, key)
+ self.testSteps = []
@property
def steps(self):
- return self.data.testSteps
+ return self.testSteps
@property
def request_opts(self):
return self.variables.get('request_opts', {})
- def load(self):
- with open(self.filename) as fh:
- data = load(self.filename, fh)
- self._load(data)
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ # del elements of state you don't want to pickle here
+ return state
- def _load(self, data):
- self.data = DictWrapper(data)
- self.variables.update(data.get('globals', {}).get('variables', {}).items())
+ def __setstate__(self, state):
+ """
+ behavior for unpickling. setting default values here
+ :param state:
+ :return:
+ """
+ # Manipulate state here before assigning it to our instance
+
+ self.__dict__.update(state)
+
+
+class TestStep(object):
+ def __init__(self):
+ """
+ Initializing a default TestStep. Not used usually but here in case we need a default object to serialize.
+ :return:
+ """
+ self.name = ""
+ self.skip = False
+ self.apiUrl = ""
+ self.dumpResponse = True
+ self.assertMap = {}
+ self.headers = {}
+ self.params = {}
+ self.method = "get"
+
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ # del elements of state you don't want to pickle here
+ return state
+
+ def __setstate__(self, state):
+ """
+ behavior for unpickling. setting default values here
+ :param state:
+ :return:
+ """
+ # Manipulate state here before assigning it to our instance
+ if not hasattr(state, 'skip'):
+ state['skip'] = False
+ self.__dict__.update(state)
-def load(filename, fh):
- if filename.endswith(".yaml"):
- return yaml.load(fh.read())
- return json.load(fh)
diff --git a/rester/manifest.py b/rester/manifest.py
index 0efeb0c..89a05a9 100644
--- a/rester/manifest.py
+++ b/rester/manifest.py
@@ -13,23 +13,23 @@ class Variables(object):
_pattern = re.compile(r'\{(\w+)\}')
def __init__(self, variables=None):
- self._variables = variables or {}
+ self.variables = variables or {}
def __iter__(self):
- for k, v in self._variables.iteritems():
+ for k, v in self.variables.iteritems():
yield k, v
def get(self, k, default):
- return self._variables.get(k, default)
+ return self.variables.get(k, default)
def update(self, values):
for k, v in values:
self.add_variable(k, v)
def add_variable(self, key, value):
- if self._variables.get(key, ''):
+ if self.variables.get(key, ''):
self.logger.warn('WARN!!! Variable : %s Already defined!!!', key)
- self._variables[key] = self.expand(value)
+ self.variables[key] = self.expand(value)
def expand(self, expression):
"""Expands logical constructions."""
@@ -37,7 +37,7 @@ def expand(self, expression):
if not is_string(expression):
return expression
- result = self._pattern.sub(lambda var: str(self._variables[var.group(1)]), expression)
+ result = self._pattern.sub(lambda var: str(self.variables[var.group(1)]), expression)
result = result.strip()
self.logger.debug('expand : %s - result : %s', expression, result)
diff --git a/rester/testcase.py b/rester/testcase.py
index c9c002a..f7c51a2 100644
--- a/rester/testcase.py
+++ b/rester/testcase.py
@@ -3,6 +3,7 @@
from rester.http import HttpClient
from rester.loader import TestSuite, TestCase
import yaml
+import jsonpickle
class bcolors:
HEADER = '\033[95m'
@@ -26,7 +27,14 @@ def run_test_suite(self, test_suite_file_name):
self._run_case(test_case)
def run_test_case(self, test_case_file):
- case = TestCase(None, test_case_file)
+ tc = TestCase(None)
+ tcenc = jsonpickle.encode(tc)
+ with open(test_case_file) as fh:
+ if test_case_file.endswith(".yaml"):
+ # TODO : probably need to replace that with a custom backend for json pickle since YAML is JSON
+ return yaml.load(fh.read())
+ case = jsonpickle.decode(fh.read())
+ case.filename = test_case_file
self._run_case(case)
def _run_case(self, case):
diff --git a/setup.py b/setup.py
index de88f82..b4abc1a 100644
--- a/setup.py
+++ b/setup.py
@@ -13,5 +13,5 @@
test_suite="test",
description='Rest API Testing',
long_description=open('README.md').read(),
- install_requires=["requests", "testfixtures", "PyYAML>=3.9"],
+ install_requires=["requests", "testfixtures", "PyYAML>=3.9", "jsonpickle"],
)