I have developed a very basic microservice using Flask framework.
A method in the application looks like this
#app.route('/add, methods=['POST'])
def add_info():
final = []
try:
info_obj.append(json.loads(request.data))
...
return jsonify(final)
Now I am attempting to write Unittest for this method and other method in this microservice. I am using import unittest to write my test.
Now here I am confused is how can I write tests to test the functionality of these http functions, which don't take regular argument of return regular results but rather fetch arguments from request data and return json based on that.
Is my approach correct? and if yes how can I test Microservices-like functionality using unittest module?
If you absolutely want unittesting, follow Patrick's guide here. But I suggest using PyTest. It's a breeze to get started. First you need a conftest.py. Then add your testfiles named test_... .py . The where your conftest is do $ pytest
Patrick has yet another PyTest + Flask guide here. You can view a demo of a conftest in a project here on how to set up db etc and a test file here
Related
I have a folder structure as below /test
----test_abc.py
----test_bcd.py
----test_cde.py
----conftest.py
conftest.py contains all spark-initiation items and in each pytest file, I use these fixtures.
For executing all pytest files, I am writing a shell script that will call all tests as shown below. Is this the correct way? I think if I call these files individually as below, it will initiate a new spark session every time I execute. Is my assumption right, Can I use the same spark session for all pytests?
bashscript.sh
pytest ./tests/test_abc.py --emr
pytest ./tests/test_bcd.py --emr
pytest ./tests/test_cde.py --emr
If you want to create a single pytest session but only call a few files, you can pass them in as positional arguments to pytest:
pytest ./tests/test_abc.py ./tests/test_bcd.py ./tests/test_cde.py --emr
In this way, session scoped fixtures will only be created once.
Here's my jupyter notebook's cell 1 (notebook is called tested.ipynb)
def func(a,b):
return a+b
Here's the testbook testing python code (tester.py):
import testbook
#testbook.testbook('tested.ipynb',execute=True)
def test_func(tb):
func=tb.ref("func")
assert func(1,2)==0
I then run the following command from terminal:
python tester.py
It should fail the unit test. But I'm not getting any output at all. No failures, no messages. How do I make the failure appear?
That's because you still need to use pytest, or another unit testing library, to run your tests. Note under 'Features' it says:
"Works with any unit testing library - unittest, pytest or nose" -SOURCE
Testbook just makes writing the unit tests easier. See here under 'Unit testing with testbook' for an example of the process of using testbook in your toolchain with pytest, although bear in mind a lot of the syntax doesn't match the the current documentation. And so instead of running python tester.py from the terminal, run the following command from terminal if you've installed pytest:
pytest tester.py
One thing I note, is that your import and decorator lines don't match the current documentation. Nevertheless, your code works when using pytest tester.py. However, it may be best to adopt the current best practices illustrated in the documentation to keep your code more robust as development continues.
I am trying to create a junit xml output file in pytest with custom attributes.
I searched for answers and found about xml_record_attribute and record_attribute.
def test(record_attribute):
record_attribute('index', '15')
...
At first I thought it would fit to my problem but then I realized it needs to be specified in each test.
I tried to do it with pytest_runtest_call hook so it would add attributes in each test run without the need to explicitly add the attributes in each test. But then it turned out that you can't use fixtures in hook (which makes sense).
Any idea how can I add an attribute to the junit xml output file without duplicating the code?
EDIT:
I have another idea of having a decorator which does that.
def xml_decorator(test):
def runner(xml_record_attribute):
xml_record_attribute('index', '15')
test()
reutrn runner
I am trying hook it with pytest_collection_modifyitems and decorate each test but it doesn't work.
def pytest_collection_modifyitems(session, config, items):
for item in items:
item.obj = xml_decorator(item.obj)
You could define a fixture that is automatically supplied to each test case:
import pytest
#pytest.fixture(autouse=True)
def record_index(record_xml_attribute):
record_xml_attribute('index', '15')
Note record_xml_attribute is not supported for xunit2, the default junit_family for pytest v6. To use record_xml_attribute with pytest v6, in pytest.ini set
[pytest]
junit_family = xunit1
I wrote some python code that works great, now I'm tasked with writing tests for that code.
My team uses mock and pytest, but I haven't really been able to copy-paste and modify something useful.
I just need a kick start, for example here is a part of my code:
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.connect(hostname='1.2.3.4', username='ubuntu')
Can someone help me write a simple unittest for this?
I understand that going forward I'd have to think about my code and write the tests as I go, but I've never done this before so I'm really just looking to get a practical start to get going.
Unit testing ensures the code works per requirements. Get the requirements and write tests to check that the code works and show that the code throws appropriate errors. You can use RobotFramework or another test automation SW to automate the tests. Some questions you might ask yourself are listed below:
ssh = paramiko.SSHClient()
Does paramiko.SSHClient exist?
is it working?
what if it fails?
do you get an error or does the SW hang?
ssh.load_system_host_keys()
Can you load the system keys?
How can you verify this?
ssh.connect(hostname='1.2.3.4', username='ubuntu')
How can you prove the connection exists?
What happens if you try to connect to another host?
Do you get an error message?
Can you logon with username 'ubuntu'?
What if you try another username?
Does the connection fail?
Do you get a generic error so you don't give crackers clues about your security?
Proof of unit testing is usually a screen capture, log entry, or some documentation showing you got the result you expected when you ran the test. Hope this helps.
you can use unit test module like below
import unittest
import paramiko
class SimpleWidgetTestCase(unittest.TestCase): #This class inherits unittest.TestCase
#setup will run first
def setUp(self):
self.ssh = paramiko.SSHClient()
self.ssh.load_system_host_keys()
self.ssh.connect(hostname='1.2.3.4', username='ubuntu')
#Your test cases goes here with 'test' prefix
def test_split(self):
#your code here
pass
#this will run after the test cases
def tearDown(self):
#your code to clean or close the connection
pass
if __name__ == '__main__':
unittest.main()
detailed information about how to use unittest can be found here https://docs.python.org/2/library/unittest.html
one suggestion: robotframework is better option to design test cases in comparison to unit test, so until unless its not compulsory , you can invest your time in Robotframework
Print can be mocked in the following way:
import unittest
import builtin
class TestSomething(unittest.TestCase):
#mock.patch('builtins.print')
def test_method(self, print_):
some_other_module.print_something()
However this means that in the python debug console (pydev debugger) and in the unit test method itself print cannot be used. This is rather inconvenient.
Is there a way to only mock the print method in some_other_module instead of in the testing module as well?
A way to sidestep this is to swap the use of print in the test module with some other function which just calls print, which I can do if there turns out to be no better solution.
#michele's "final solution" has an even cleaner alternative which works in my case:
from unittest import TestCase
from unittest.mock import patch
import module_under_test
class MyTestCase(TestCase):
#patch('module_under_test.print', create=True)
def test_something(self, print_):
module_under_test.print_something()
print_.assert_called_with("print something")
Yes you can! ... But just because you are using Python 3. In Python 3 print is a function and you can rewrite it without change the name. To understand the final solution I'll describe it step by step to have a final flexible and non intrusive solution.
Instrument Module
The trick is add at the top of your module that you will test a line like:
print = print
And now you can patch just print of your module. I wrote a test case where the mock_print_module.py is:
print = print
def print_something():
print("print something")
And the test module (I'm using autospec=True just to avoid errors like mock_print.asser_called_with):
from unittest import TestCase
from unittest.mock import patch
import mock_print_module
class MyTestCase(TestCase):
#patch("mock_print_module.print",autospec=True)
def test_something(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
I don't want to change my module but just patch print without lose functionalities
You can use patch on "builtins.print" without lose print functionality just by use side_effect patch's attribute:
#patch("builtins.print",autospec=True,side_effect=print)
def test_somethingelse(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
Now you can trace your prints call without lose the logging and pydev debugger. The drawback of that approach is that you must fight against lot of noise to check your interested the print calls. Moreover you cannot chose what modules will be patched and what not.
Both modes don't work together
You cannot use both way together because if you use print=print in your module you save builtins.print in print variable at the load module time. Now when you patch builtins.print the module still use the original saved one.
If you would have a chance to use both you must wrap original print and not just record it. A way to implement it is use following instead of print=print:
import builtins
print = lambda *args,**kwargs:builtins.print(*args,**kwargs)
The Final Solution
Do we really need to modify the original module to have a chance to patch all print calls in it? No, we can do it without change the module to test anyway. The only thing that we need is injecting a local print function in the module to override the builtins's one: we can do it in the test module instead of the the module to test. My example will become:
from unittest import TestCase
from unittest.mock import patch
import mock_print_module
import builtins
mock_print_module.print = lambda *args,**kwargs:builtins.print(*args,**kwargs)
class MyTestCase(TestCase):
#patch("mock_print_module.print",autospec=True)
def test_something(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
#patch("builtins.print",autospec=True,side_effect=print)
def test_somethingelse(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
and mock_print_module.py can be the clean original version with just:
def print_something():
print("print something")