Recognizing module level tests - python

How can I get test_greet to run in the below; note: test_one(when uncommented) is seen and run by the test runner; to be specific, I want the line unittest.main() to correctly pick up the module level test (test_greet).
import unittest
#class MyTests(unittest.TestCase):
# def test_one(self):
# assert 1==2
def test_greet():
assert 1==3
if __name__=="__main__":
unittest.main()

Let's say i have a file called MyTests.py as below:
import unittest
class MyTests(unittest.TestCase):
def test_greet(self):
self.assertEqual(1,3)
Then:
Open a CMD in the folder that MyTests.py exists
Run python -m unittest MyTests
Please note that, all your tests must have the test_ otherwise, it will not be run.

Related

How to run unittest tests using coverage API

I am trying to generate a coverage report using the coverage-API (https://coverage.readthedocs.io/en/6.3.2/api.html#api).
The simple use-case described on the linked page tells me to wrap my executed code inside the snippet they provide. I am using unittest.main() to execute tests. The below code runs without an error but neither is any report information created nor is print("Done.") executed.
I guess unittest.main() calls a sys.exit() somewhere along the way? How does one use the API to execute all unittest-tests?
Example
import coverage
import unittest
def func(input):
return input
class testInput(unittest.TestCase):
def test_func(self):
self.assertEqual(func(1), 1)
if __name__ == '__main__':
cov = coverage.Coverage()
cov.start()
unittest.main()
cov.stop()
cov.save()
cov.html_report()
print("Done.")
Yes, it looks like unittest.main() is calling sys.exit(). Are you sure you need to use the coverage API? You can skip coverage and most of unittest:
# foo.py
import unittest
def func(input):
return input
class testInput(unittest.TestCase):
def test_func(self):
self.assertEqual(func(1), 1)
Then:
$ python -m coverage run -m unittest foo.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
$ python -m coverage report -m
Name Stmts Miss Cover Missing
--------------------------------------
foo.py 6 0 100%
--------------------------------------
TOTAL 6 0 100%
To get the example to work you can simply put the call to unittest.main() in a try/except statement so the cov.stop() will be called once the unit test has completed.
# testInput.py
import coverage
import unittest
def func(input):
return input
class testInput(unittest.TestCase):
def test_func(self):
self.assertEqual(func(1), 1)
if __name__ == '__main__':
cov = coverage.Coverage()
cov.start()
try:
unittest.main()
except: # catch-all except clause
pass
cov.stop()
cov.save()
cov.html_report()
print("Done.")
This will run as desired, including printing Done. at the end:
python3 testInput.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Done.
The HTML coverage report will have been created in the htmlcov directory in the current working directory.

Monkeypatch an executable in Python pytest

In Python 3.10, I have a function like:
from shutil import which
def my_func():
if which('myexecutable.sh'):
# do stuff
else:
# do other stuff
I would like to write a unit test with Pytest that runs the first part code even though the executable is not present. What is the best way to do this?
I know that I can use monkeypatch.setenv() to set an environment variable, but that's not going to make the which() check pass. There's also the added challenge of making sure this is compatible on Windows and Linux.
You could try like this:
# in script file
from shutil import which
def myfunc():
if which("myexecutable.sh"):
return "OK"
else:
...
# in test file
import pytest
from script import myfunc
#pytest.fixture
def which(mocker):
return mocker.patch("script.which", autospec=True)
def test_myfunc(which):
assert myfunc() == "OK"
Running pytest outputs: 1 passed

Run a single test from within module main

Suppose I have myclass_test.py with Nose tests with following lines:
import nose
class TestMyClass:
def test_method(self):
assert true
def test_another_method(self):
assert true
if __name__ == "__main__":
nose.runmodule()
So I can run python myclass_test.py and have all of my tests to pass. Everything work as expected.
What should I pass to runmodule() to run a single test (for example test_method)?
If it is impossible with Nose is it possible with some other unit-test framework on the same manner (with runner inside module)?
P.S. I actually run not python myclass_test.py but sage myclass_test.sage. These are SageMath interpreter and SageMath file. SageMath code is basically Python code and myclass_test.sage is finally preparsing to myclass_test.sage.py and run with embedded to Sage Python interpreter.
So it isn't recognizable as module by Python interpreter so passing something like sage myclass_test.sage TestMyClass.test_method or sage myclass_test.sage myclass_test.sage:TestMyClass.test_method or sage myclass_test.sage myclass_test.sage.py:TestMyClass.test_method isn't working (by the same reason one cannot directly import sage file as module). Even though Nose passing argv to runmodule() automatically.
P.P.S. Also I cannot use external runner like nosetests -q -s sage myclass_test.sage:TestMyClass.test_method.
I haven't used nose, but here is a possibility to do it with unittest:
import unittest
class TestMyClass(unittest.TestCase):
def test_method(self):
assert True
def test_another_method(self):
assert True
if __name__ == '__main__':
suite = unittest.TestSuite([TestMyClass('test_method')])
unittest.TextTestRunner().run(suite)
And here is the same with pytest:
import pytest
class TestMyClass:
def test_method(self):
assert True
def test_another_method(self):
assert True
if __name__ == '__main__':
pytest.main([f'{__file__}::TestMyClass::test_method'])
Ok, I also checked with nose, and this seems to work:
import nose
from nose.suite import ContextSuite
class TestMyClass:
def test_method(self):
assert True
def test_another_method(self):
assert True
if __name__ == '__main__':
suite = ContextSuite([TestMyClass.test_method])
nose.runmodule(suite=suite)
though the output is strange - while it certainly runs the test, it says "Ran 0 tests".

unittest is not running my tests after reorganising file structure

Here is my structure:
directory/
__init__.py (blank)
myClass.py
test/
UnitTest.py
IntegrationTest.py
__init__.py (blank)
Let's use UnitTest as the example (both of them result in 0 tests). Feel free to critique my imports, I was fiddling around with it forever to import the class correctly so I can execute the UnitTest script. Also yes, I did copy a lot of code from other stack exchange questions in my search.
Before I moved to this structure, I had everything in one directory, so I know the test file works (outside of the imports)
import sys
from pathlib import Path
if __name__ == '__main__' and __package__ is None:
file = Path(__file__).resolve()
parent, top = file.parent, file.parents[2]
sys.path.append(str(top))
try:
sys.path.remove(str(parent))
except ValueError: # Already removed
pass
import directory.test
__package__ = 'directory.test'
from myClass import myClass
import unittest
from unittest import mock
print("Running Unit Tests...")
mc = myClass(params)
def mockingResponse(*args, **kwargs):
class MockResponse:
def __init__(self, json_data, status_code):
self.json_data = json_data
self.status_code = status_code
def json(self):
return self.json_data
#if/elses that return value
#mock.patch('myClass.requests.get', side_effect = mockingResponse)
class unitTest(unittest.TestCase):
print("this should run before test call")
def testsomefunc():
response = mc.somefunc()
differenceSet = set(response) ^ set(mockrespose from if/elses)
assert len(differenceSet) == 0
def testotherfunc():
#7 tests in total, same layout, different mockresponse
print("is this actually running")
unittest.main()
Then I get this in terminal:
privacy:~/git/directory$ python3 -m test.UnitTest.py
Running Unit Tests...
this should run before test call
is this actually running
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Checking online, I know it's not the issue of not having my names start with test and it's not because I have it indented wrong. Can't find much else in my search.
My only other thought is that it might be because I ran it as a module, but if I don't I have more problems with importing myClass. Of the many solutions I've tried for importing, this is the only one that has worked thus far.

pytest: run test from code, not from command line

Is it possible to run tests from code using pytest? I did find pytest.main, but it's just a command line interface available from code. I would like to pass a test class / function from the code.
In unittest it's possible this way:
from unittest import TestLoader, TestCase, TestResult
class TestMy(TestCase):
def test_silly(self):
assert False
runner = TestLoader()
test_suite = runner.loadTestsFromTestCase(TestMy)
test_result = TestResult()
test_suite.run(test_result)
print(test_result)
Yes it's possible, that way for instance:
from pytest import main
class TestMy:
def test_silly(self):
assert False
main(['{}::{}'.format(__file__, TestMy.__name__)])
You can pass any argument to main as if called from command line.

Categories