Skip to content

Commit 890cdb9

Browse files
authored
Merge pull request #7 from suaaa7/python_test_example
Python UnitTest Example
2 parents 7f5a18c + 08da750 commit 890cdb9

File tree

17 files changed

+245
-2
lines changed

17 files changed

+245
-2
lines changed

.gitignore

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ project/plugins/project/
1717
.worksheet
1818
.idea
1919

20-
# pycaret_example
20+
# pycaret_example & python_test_example
2121
*.csv
2222
pycaret_example/notebook/.ipynb_checkpoints
23-
pycaret_example/notebook/catboost_info
23+
pycaret_example/notebook/catboost_info
24+
__pycache__

python_test_example/Dockerfile

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM python:3.7-slim
2+
3+
RUN pip install --no-cache-dir --upgrade pip setuptools wheel \
4+
&& pip install --no-cache-dir requests flask

python_test_example/Makefile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
image = unittest
2+
tag = 0.1.0
3+
curdir := `pwd`
4+
5+
.PHONY: dbuild
6+
dbuild:
7+
docker build -t $(image):$(tag) .
8+
9+
.PHONY: drun
10+
drun:
11+
docker run --rm -it -v $(curdir):/opt -w /opt $(image):$(tag) bash
12+
13+
.PHONY: all_test
14+
all_test:
15+
python3 -m unittest discover --verbose --pattern "*_test.py"

python_test_example/flask_app/__init__.py

Whitespace-only changes.

python_test_example/flask_app/app.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import json
2+
3+
from flask import Flask, jsonify, make_response
4+
5+
from .service import Service
6+
7+
app = Flask(__name__)
8+
9+
print('Instantiate Service in app')
10+
service = Service()
11+
12+
@app.route('/', methods=['GET'])
13+
def index():
14+
message = {'message': 'OK'}
15+
return make_response(jsonify(message), 200)
16+
17+
@app.route('/predict', methods=['POST'])
18+
def predict():
19+
print('Call predict in app')
20+
target = 'A'
21+
if not service.check_model():
22+
return make_response(jsonify({'message': 'Service Unavailable'}), 503)
23+
result = {'result': service.predict(target)}
24+
return make_response(jsonify(result), 200)
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import json
2+
from unittest import TestCase, main
3+
from unittest.mock import MagicMock, patch
4+
5+
print('In app_test')
6+
7+
class AppTestCase(TestCase):
8+
def setUp(self):
9+
print('Call setUp in AppTestCase')
10+
11+
self.load_model_patcher = patch('flask_app.model.load_model')
12+
self.load_model_m = self.load_model_patcher.start()
13+
self.load_model_m.return_value = 'test'
14+
15+
from flask_app.app import app
16+
self.client = app.test_client()
17+
18+
def tearDown(self):
19+
self.load_model_patcher.stop()
20+
21+
def test_index(self):
22+
response = self.client.get('/')
23+
self.assertEqual(response.status_code, 200)
24+
25+
def test_index_content(self):
26+
response = self.client.get('/')
27+
self.assertEqual(response.content_type, 'application/json')
28+
29+
def test_predict(self):
30+
response = self.client.post('/predict')
31+
self.assertEqual(response.status_code, 200)
32+
33+
def test_predict_content(self):
34+
response = self.client.post('/predict')
35+
self.assertEqual(response.content_type, 'application/json')
36+
37+
def test_predict_data(self):
38+
response = self.client.post('/predict')
39+
self.assertIsInstance(json.loads(response.data)['result'], float)
40+
41+
@patch('flask_app.service.Service.check_model', return_value=False)
42+
def test_predict_503(self, mock: MagicMock):
43+
response = self.client.post('/predict')
44+
self.assertEqual(response.status_code, 503)
45+
46+
if __name__ == '__main__':
47+
main()
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def load_model():
2+
print('Call load_model in model')
3+
return 'model'
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from random import randint
2+
3+
from .model import load_model
4+
5+
print('In service')
6+
7+
class Service:
8+
def __init__(self):
9+
print('Init Service')
10+
self.model = load_model()
11+
print(f'self.model: {self.model}')
12+
13+
def predict(self, target: str) -> float:
14+
print('Call predict in service')
15+
return randint(0, 1000) / 1000.0
16+
17+
def check_model(self) -> bool:
18+
if self.model:
19+
return True
20+
return False

python_test_example/i76_testcase_subclass/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def concat(str1: str, str2: str) -> str:
2+
if not isinstance(str1, str) and isinstance(str2, str):
3+
raise TypeError
4+
5+
return str1 + str2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from unittest import TestCase, main
2+
3+
from i76_testcase_subclass.utils import concat
4+
5+
class UtilsTestCase(TestCase):
6+
def test_good_for_concat(self):
7+
test_cases = [
8+
(('a', 'b'), 'ab'),
9+
(('test', 'case'), 'testcase'),
10+
]
11+
for value, expected in test_cases:
12+
with self.subTest(value):
13+
self.assertEqual(expected, concat(value[0], value[1]))
14+
15+
def test_bad_for_concat(self):
16+
test_cases = [
17+
(('a', 2), TypeError),
18+
((1, 'b'), TypeError),
19+
]
20+
for value, exception in test_cases:
21+
with self.subTest(value):
22+
with self.assertRaises(exception):
23+
concat(value[0], value[1])
24+
25+
if __name__ == '__main__':
26+
main()

python_test_example/i77_setup_and_teardown/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from unittest import TestCase, main
2+
3+
class Integration2Test(TestCase):
4+
@classmethod
5+
def setUpClass(cls):
6+
print('* Class setup')
7+
8+
@classmethod
9+
def tearDownClass(cls):
10+
print('* Class clean-up')
11+
12+
def setUp(self):
13+
print('** Test setup')
14+
15+
def tearDown(self):
16+
print('** Test clean-up')
17+
18+
def test_1(self):
19+
print('** Test 1')
20+
21+
def test_2(self):
22+
print('** Test 2')
23+
24+
if __name__ == '__main__':
25+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from unittest import TestCase, main
2+
3+
def setUpModule():
4+
print('* Module setup')
5+
6+
def tearDownModule():
7+
print('* Module clean-up')
8+
9+
class IntegrationTest(TestCase):
10+
def setUp(self):
11+
print('** Test setup')
12+
13+
def tearDown(self):
14+
print('** Test clean-up')
15+
16+
def test_1(self):
17+
print('** Test 1')
18+
19+
def test_2(self):
20+
print('** Test 2')
21+
22+
if __name__ == '__main__':
23+
main()

python_test_example/mock/__init__.py

Whitespace-only changes.

python_test_example/mock/my_class.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import requests
2+
3+
class MyClass:
4+
def fetch_json(self, url: str) -> dict:
5+
response = requests.get(url)
6+
return response.json()
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from unittest import TestCase, main
2+
from unittest.mock import MagicMock, call, patch
3+
4+
from mock.my_class import MyClass
5+
6+
class MyClassTestCase(TestCase):
7+
def mocked_requests_get(*args, **kwargs):
8+
class MockResponse:
9+
def __init__(self, json_data, status_code):
10+
self.json_data = json_data
11+
self.status_code = status_code
12+
13+
def json(self):
14+
return self.json_data
15+
16+
if args[0] == 'http://example.com/test.json':
17+
return MockResponse({'key1': 'value1'}, 200)
18+
elif args[0] == 'http://example.com/another_test.json':
19+
return MockResponse({'key2': 'value2'}, 200)
20+
21+
return MockResponse(None, 404)
22+
23+
@patch('requests.get', side_effect=mocked_requests_get)
24+
def test_fetch_json(self, mock_get: MagicMock):
25+
my_class = MyClass()
26+
27+
json_data = my_class.fetch_json('http://example.com/test.json')
28+
self.assertEqual(json_data, {'key1': 'value1'})
29+
json_data = my_class.fetch_json('http://example.com/another_test.json')
30+
self.assertEqual(json_data, {'key2': 'value2'})
31+
json_data = my_class.fetch_json('http://no_example.com/test.json')
32+
self.assertIsNone(json_data)
33+
34+
self.assertIn(
35+
call('http://example.com/test.json'), mock_get.call_args_list
36+
)
37+
self.assertIn(
38+
call('http://example.com/another_test.json'), mock_get.call_args_list
39+
)
40+
41+
self.assertEqual(len(mock_get.call_args_list), 3)
42+
43+
if __name__ == '__main__':
44+
main()

0 commit comments

Comments
 (0)