diff --git a/requirements.txt b/requirements.txt index 8e75573..e60629a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,5 +10,6 @@ PyYAML rdflib rdflib-jsonld numpy -scipy +scip scikit-learn +responses diff --git a/senpy/client.py b/senpy/client.py index 892e77a..43414f8 100644 --- a/senpy/client.py +++ b/senpy/client.py @@ -13,7 +13,7 @@ class Client(object): return self.request('/', method=method, input=input, **kwargs) def evaluate(self, input, method='GET', **kwargs): - return self.request('/evaluate', method = method, input=input, **kwargs) + return self.request('/evaluate', method=method, input=input, **kwargs) def plugins(self, *args, **kwargs): resp = self.request(path='/plugins').plugins @@ -24,7 +24,7 @@ class Client(object): return {d.name: d for d in resp} def request(self, path=None, method='GET', **params): - url = '{}{}'.format(self.endpoint, path) + url = '{}{}'.format(self.endpoint.rstrip('/'), path) response = requests.request(method=method, url=url, params=params) try: resp = models.from_dict(response.json()) diff --git a/senpy/plugins/__init__.py b/senpy/plugins/__init__.py index 84d4860..6a0e1ae 100644 --- a/senpy/plugins/__init__.py +++ b/senpy/plugins/__init__.py @@ -24,6 +24,7 @@ import nltk from .. import models, utils from .. import api from .. import gsitk_compat +from .. import testing logger = logging.getLogger(__name__) @@ -143,14 +144,23 @@ class Plugin(with_metaclass(PluginMeta, models.Plugin)): self.log.warn('Test case failed:\n{}'.format(pprint.pformat(case))) raise - def test_case(self, case): + def test_case(self, case, mock=testing.MOCK_REQUESTS): entry = models.Entry(case['entry']) given_parameters = case.get('params', case.get('parameters', {})) expected = case.get('expected', None) should_fail = case.get('should_fail', False) + responses = case.get('responses', []) + try: params = api.parse_params(given_parameters, self.extra_params) - res = list(self.analyse_entries([entry, ], params)) + + method = partial(self.analyse_entries, [entry, ], params) + + if mock: + res = list(method()) + else: + with testing.patch_all_requests(responses): + res = list(method()) if not isinstance(expected, list): expected = [expected] diff --git a/senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py b/senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py index 1057079..d40fd15 100644 --- a/senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py +++ b/senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py @@ -4,6 +4,8 @@ import json from senpy.plugins import SentimentPlugin from senpy.models import Sentiment +ENDPOINT = 'http://www.sentiment140.com/api/bulkClassifyJson' + class Sentiment140(SentimentPlugin): '''Connects to the sentiment140 free API: http://sentiment140.com''' @@ -26,7 +28,7 @@ class Sentiment140(SentimentPlugin): def analyse_entry(self, entry, params): lang = params["language"] - res = requests.post("http://www.sentiment140.com/api/bulkClassifyJson", + res = requests.post(ENDPOINT, json.dumps({ "language": lang, "data": [{ @@ -52,18 +54,6 @@ class Sentiment140(SentimentPlugin): entry.language = lang yield entry - def test(self, *args, **kwargs): - ''' - To avoid calling the sentiment140 API, we will mock the results - from requests. - ''' - from senpy.testing import patch_requests - expected = {"data": [{"polarity": 4}]} - with patch_requests(expected) as (request, response): - super(Sentiment140, self).test(*args, **kwargs) - assert request.called - assert response.json.called - test_cases = [ { 'entry': { @@ -77,6 +67,9 @@ class Sentiment140(SentimentPlugin): 'marl:hasPolarity': 'marl:Positive', } ] - } + }, + 'responses': [{'url': ENDPOINT, + 'method': 'POST', + 'json': {'data': [{'polarity': 4}]}}] } ] diff --git a/senpy/testing.py b/senpy/testing.py index 9190637..6777a0d 100644 --- a/senpy/testing.py +++ b/senpy/testing.py @@ -1,36 +1,31 @@ -try: - from unittest.mock import patch, MagicMock -except ImportError: - from mock import patch, MagicMock - from past.builtins import basestring - -import json -from contextlib import contextmanager +import os +import responses as requestmock from .models import BaseModel -@contextmanager -def patch_requests(value, code=200): - success = MagicMock() - if isinstance(value, BaseModel): - value = value.jsonld() - if not isinstance(value, basestring): - data = json.dumps(value) - else: - data = value +MOCK_REQUESTS = os.environ.get('MOCK_REQUESTS', '').lower() in ['no', 'false'] + - success.json.return_value = value +def patch_all_requests(responses): - success.status_code = code - success.content = data - success.text = data + patched = requestmock.RequestsMock() - method_mocker = MagicMock() - method_mocker.return_value = success - with patch.multiple('requests', request=method_mocker, - get=method_mocker, post=method_mocker): - yield method_mocker, success - assert method_mocker.called + for response in responses or []: + args = response.copy() + if 'json' in args and isinstance(args['json'], BaseModel): + args['json'] = args['json'].jsonld() + args['method'] = getattr(requestmock, args.get('method', 'GET')) + patched.add(**args) + return patched + + +def patch_requests(url, response, method='GET', status=200): + args = {'url': url, 'method': method, 'status': status} + if isinstance(response, basestring): + args['body'] = response + else: + args['json'] = response + return patch_all_requests([args]) diff --git a/tests/test_client.py b/tests/test_client.py index d2a48f8..710f894 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -14,22 +14,15 @@ class ModelsTest(TestCase): def test_client(self): endpoint = 'http://dummy/' client = Client(endpoint) - with patch_requests(Results()) as (request, response): + with patch_requests('http://dummy/', Results()): resp = client.analyse('hello') assert isinstance(resp, Results) - request.assert_called_with( - url=endpoint + '/', method='GET', params={'input': 'hello'}) - with patch_requests(Error('Nothing')) as (request, response): + with patch_requests('http://dummy/', Error('Nothing')): try: client.analyse(input='hello', algorithm='NONEXISTENT') raise Exception('Exceptions should be raised. This is not golang') except Error: pass - request.assert_called_with( - url=endpoint + '/', - method='GET', - params={'input': 'hello', - 'algorithm': 'NONEXISTENT'}) def test_plugins(self): endpoint = 'http://dummy/' @@ -37,11 +30,8 @@ class ModelsTest(TestCase): plugins = Plugins() p1 = AnalysisPlugin({'name': 'AnalysisP1', 'version': 0, 'description': 'No'}) plugins.plugins = [p1, ] - with patch_requests(plugins) as (request, response): + with patch_requests('http://dummy/plugins', plugins): response = client.plugins() assert isinstance(response, dict) assert len(response) == 1 assert "AnalysisP1" in response - request.assert_called_with( - url=endpoint + '/plugins', method='GET', - params={}) diff --git a/tests/test_test.py b/tests/test_test.py index c1d2d5b..08d5355 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -5,28 +5,29 @@ import json from senpy.testing import patch_requests from senpy.models import Results +ENDPOINT = 'http://example.com' + class TestTest(TestCase): def test_patch_text(self): - with patch_requests('hello'): - r = requests.get('http://example.com') + with patch_requests(ENDPOINT, 'hello'): + r = requests.get(ENDPOINT) assert r.text == 'hello' - assert r.content == 'hello' def test_patch_json(self): r = Results() - with patch_requests(r): - res = requests.get('http://example.com') - assert res.content == json.dumps(r.jsonld()) + with patch_requests(ENDPOINT, r): + res = requests.get(ENDPOINT) + assert res.text == json.dumps(r.jsonld()) js = res.json() assert js assert js['@type'] == r['@type'] def test_patch_dict(self): r = {'nothing': 'new'} - with patch_requests(r): - res = requests.get('http://example.com') - assert res.content == json.dumps(r) + with patch_requests(ENDPOINT, r): + res = requests.get(ENDPOINT) + assert res.text == json.dumps(r) js = res.json() assert js assert js['nothing'] == 'new'