diff --git a/README.rst b/README.rst index 2234aeb..42ca390 100755 --- a/README.rst +++ b/README.rst @@ -20,7 +20,25 @@ In a nutshell .. code-block:: python import requests - import grappa-http import grappa + import grappa-http import should + + # Perform an HTTP request + res = requests.get('http://httpbin.org/user-agent') + + # Test response status to be OK + res | should.be.ok + # Or alternatively using the status code + res | should.have.status(200) + + # Test response body content type + res | should.have.content('json') + + # Test response headers + res | should.have.header('Content-Type').that.should.be.equal('application/json') + res | should.have.header('Server').that.should.contain('nginx') + + # Test response body + res | should.have.json.equal.to({'user-agent': 'requests'}) Features -------- diff --git a/grappa_http/adapters/requests.py b/grappa_http/adapters/requests.py index 06f9554..cf99e47 100644 --- a/grappa_http/adapters/requests.py +++ b/grappa_http/adapters/requests.py @@ -45,11 +45,11 @@ def headers(self): @property def body(self): - return self.res.body() + return self.res.text @property def json(self): - return self.res.json(indent=4) + return self.res.json() @property def elapsed(self): diff --git a/grappa_http/operators/__init__.py b/grappa_http/operators/__init__.py index 8856959..c9e7564 100644 --- a/grappa_http/operators/__init__.py +++ b/grappa_http/operators/__init__.py @@ -9,6 +9,7 @@ operators = ( # Module name # Operator class to import ('attributes', ), + ('body', 'BodyOperator', 'JsonOperator'), ('method', 'MethodOperator'), ('header', 'HeaderOperator'), ('content', 'ContentTypeOperator'), diff --git a/grappa_http/operators/body.py b/grappa_http/operators/body.py new file mode 100644 index 0000000..e9c967d --- /dev/null +++ b/grappa_http/operators/body.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +from .base import BaseOperator, Operator + + +class BodyOperator(BaseOperator): + """ + Asserts HTTP response body content. + + Example:: + + # Should style + res | should.be.content('json') + res | should.be.content.of('xml') + res | should.have.response.content.type('html') + res | should.have.response.content.type('application/json') + res | should.have.response.content.equal.to('application/json') + + # Should style - negation form + res | should.not_have.content('json') + res | should.not_have.content.of('json') + + # Expect style + res | expect.to.have.content('json') + res | expect.to.have.content.of('xml') + res | expect.to.have.content.type('html') + + # Expect style - negation form + res | expect.to.not_have.content('json') + res | expect.to.not_have.content.of('xml') + res | expect.to.not_have.content.type('html') + """ + + # Defines operator kind + kind = Operator.Type.MATCHER + + # Operator keywords + operators = ('body', 'data') + + # Operator aliases + aliases = ('equal', 'to', 'be', 'of') + + # Error message templates + expected_message = Operator.Dsl.Message( + 'a response content type that matches with "{value}"', + 'a response content type that does not match with "{value}"', + ) + + # Subject message template + subject_message = Operator.Dsl.Message( + 'a response content type equal to "{value}"', + ) + + def _match(self, res, expected): + self.ctx.show_diff = True + self.ctx.value = res.body + + return expected == res.body, [] + + +class JsonOperator(BaseOperator): + """ + Asserts HTTP response body content. + + Example:: + + # Should style + res | should.be.content('json') + res | should.be.content.of('xml') + res | should.have.response.content.type('html') + res | should.have.response.content.type('application/json') + res | should.have.response.content.equal.to('application/json') + + # Should style - negation form + res | should.not_have.content('json') + res | should.not_have.content.of('json') + + # Expect style + res | expect.to.have.content('json') + res | expect.to.have.content.of('xml') + res | expect.to.have.content.type('html') + + # Expect style - negation form + res | expect.to.not_have.content('json') + res | expect.to.not_have.content.of('xml') + res | expect.to.not_have.content.type('html') + """ + + # Defines operator kind + kind = Operator.Type.MATCHER + + # Operator keywords + operators = ('json', 'json_body') + + # Operator aliases + aliases = ('equal', 'to', 'be', 'of') + + # Error message templates + expected_message = Operator.Dsl.Message( + 'a response content type that matches with "{value}"', + 'a response content type that does not match with "{value}"', + ) + + # Subject message template + subject_message = Operator.Dsl.Message( + 'a response content type equal to "{value}"', + ) + + def _match(self, res, expected): + self.ctx.show_diff = True + self.ctx.value = res.json + + return res.json == expected, ['invalid json bodies'] diff --git a/grappa_http/operators/content.py b/grappa_http/operators/content.py index 9f2a6eb..42efacd 100644 --- a/grappa_http/operators/content.py +++ b/grappa_http/operators/content.py @@ -46,7 +46,7 @@ class ContentTypeOperator(BaseOperator): kind = Operator.Type.MATCHER # Operator keywords - operators = ('content',) + operators = ('content', 'content_type', 'ctype') # Operator aliases aliases = ('type', 'equal', 'to', 'be', 'of') diff --git a/grappa_http/operators/header.py b/grappa_http/operators/header.py index ad8068e..922185a 100644 --- a/grappa_http/operators/header.py +++ b/grappa_http/operators/header.py @@ -69,10 +69,6 @@ def match_header(self, headers, key, value, includes=False): return True, None - # def differ(self): - # print('>>>> differ!') - # return self.header - def _match(self, res, header, value=None, includes=False): # Set subject headers self.subject = res.headers.get(header) @@ -104,11 +100,10 @@ def _match(self, res, header, value=None, includes=False): # Set headers self.header = header - # self.ctx.value = {} # Assign match value if value is None: self.ctx.value = values[0] if len(values) == 1 else values - self.ctx.yielded = values[0] if len(values) == 1 else values + # self.ctx.yielded = values[0] if len(values) == 1 else values return passed, reasons diff --git a/tests/operators/body_test.py b/tests/operators/body_test.py new file mode 100644 index 0000000..2595c63 --- /dev/null +++ b/tests/operators/body_test.py @@ -0,0 +1,13 @@ +import pook +import pytest +import requests + + +def test_body_operator(should): + pook.get('foo.com', reply=200, response_json={'foo': 'bar'}) + res = requests.get('http://foo.com') + + res | should.have.json.equal.to({'foo': 'bar'}) + + with pytest.raises(AssertionError): + res | should.have.body('foo')