Why doesn't the test work without if __name__ == '__main__'? - python

I can't figure out why without the line if __name__ == '__main__': before unittest.main() the test does not find?
I am using the latest version of PyCharm. I know that in order for the test to work in PyCharm, you can not add these lines at all, but I want to deal with the logic itself: why without the line if __name__ == '__main__': the result is as in the screenshot, but if you add it, then everything works?
Code:
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase):
"""Tests for 'name_function.py'."""
def test_first_last_name(self):
"""Are names like 'Janis Joplin' working correctly?"""
formatted_name = get_formatted_name('janis', 'joplin')
self.assertEqual(formatted_name, 'Janis Joplin')
unittest.main()
There is only one function in the name_function module:
def get_formatted_name(first, last):
"""Builds a formatted full name."""
full_name = f"{first} {last}"
return full_name.title()
Result:
No tests were found
/Users/xxx/Documents/PycharmProjects/Book/venv/bin/python
"/Applications/PyCharm
CE.app/Contents/plugins/python-ce/helpers/pycharm/_jb_unittest_runner.py"
--path /Users/xxx/Documents/PycharmProjects/Book/testing.py
Testing started at 00:22 ...
-------------------------------------------------------------------> Ran 0 tests in 0.000s
OK
Launching unittests with arguments python -m unittest
/Users/xxx/Documents/PycharmProjects/Book/testing.py in
/Users/xxx/Documents/PycharmProjects/Book
Process finished with exit code 0
Empty suite
Empty suite
I am running the testing.py module as the main program, but judging by the answer line PyCharm is running the test via python -m unittest testing.NamesTestCase
I additionally checked the value of the global variable __name__ and indeed it has the value testing, as if testing was imported. Although I launch it initially.
Please explain why in this case the startup script differs from the standard one and when testing.py starts it runs it through unittest? I really want to finally understand this issue. Also don't understand why, in this case, if it initially runs through unittest, unittest.main() doesn't run normally without additional checking if __name__ == '__main__':?

Related

Unittest hangs when testing input, but same input runs perfectly fine on main program

Whenever I run the following unittest, it just hangs and nothing happens. The program itself returns an output withing 1 sec, but I've ran this for 5+ mins without anything happening.
import unittest
from syntax import *
class SyntaxTest(unittest.TestCase):
def testCase(self):
self.assertEqual(CheckSyntax('C'), 'Formeln är syntaktiskt korrekt')
if __name__ == '__main__':
unittest.main()
The code itself is a simple syntax checker for molecules. It's not completely finished yet, but runs fine for the specified input above ('C'). I've uploaded it here: https://pastebin.com/rD3f6PWL
You are importing your syntax module, that means it will run all the function and class definitions (not the code inside them) and any other code in the main body. the last line of the syntax module is
print(CheckSyntax(input()))
So when you import the syntax module this line will be executed and will be waiting for input. If you only mean this line to be executed when you run syntax as a script then you would be better to wrap it in an if main block like
if __name__ == "__main__":
print(CheckSyntax(input()))
this way the last line will only be run if the script is being run directly and not if its being imported to be used somewhere else.

Use `__name__ == "__main__":` within a class that implements `unittest.TestCase`

I have a unittest.TestCase with a very heavy setup class
I would like to run it using a mock if the code is launched as main, and run the full data check if it runs trough an import as following:
import unittest
from utilities import create_full_data,create_mock_data
if __name__ == "__main__":
print(' I want to run this block if code starts from here')
data_to_check=create_mock_data()
else:
print(' I want to run this block if imported')
data_to_check = create_full_data()
class Test_payer_seg(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.data_to_test = data_to_check
def test_data_qaulity(self):
self.assertTrue(1==1)
The problem seems to occur since under the hood nosetests detects is as python tests and runs it as an internal process, hence __name__ can't possibly become 'main'.
How can I create a flow that runs it as mock if it is launched a main?
Note that this issue may happen since I'm running it using pycharm
pycharm recognize it as unit test and automatically runs it using the installed unit test package.
You can create a new Python configuration with the script full path and working directory. This will run your main section without using the unit tests package.

Is it possible to run Robot Framework's unittests with Pytest?

Robot Framework has a great set of unit tests which are implemented using Python's unittest module. I wonder if these tests can be run with Pytest and if somebody has already tried to do so. At least Pytest's docu says that it can deal with regular Python unittest.
EDIT: To be more precise. I would like to run Robot's own over 1000 unit tests with Pytest instead of with Python's unittest module. E.g. now you have to run python run.py inside the utest folder of RF's repo to execute all unit tests. So what I am actually asking for is how to modify run.py so that it uses the Pytest framework instead of the unittest framework?
I think the most tricky part is here:
if __name__ == '__main__':
docs, vrbst = parse_args(sys.argv[1:])
tests = get_tests()
suite = unittest.TestSuite(tests)
runner = unittest.TextTestRunner(descriptions=docs, verbosity=vrbst)
result = runner.run(suite)
rc = len(result.failures) + len(result.errors)
if rc > 250:
rc = 250
sys.exit(rc)
especially:
suite = unittest.TestSuite(tests)
runner = unittest.TextTestRunner(descriptions=docs, verbosity=vrbst)
I already can run single test by pointing pytest to concrete test file, e.g. pytest utest/api/test_exposed_api.py. But if I try to run all unit test in utest folder with pytest utest/ I just get errors and warnings :(
Short answer: Yes it is! :)))
Long answer: I had to edit only two lines in run.py and was almost happy with the result
add import pytest below import unittest
edit the if __name__ == '__main__': section so that it looks like that:
if __name__ == '__main__':
docs, vrbst = parse_args(sys.argv[1:])
tests = get_tests()
pytest.main()
Then in utest folder just call python run.py and the tests run :)))
1532 passed, 45 warnings, 1 error in 9.70 seconds
Obviously there is more stuff which is not necessary for pytest in run.py and could be wiped out ... but me no python expert (yet)

PyCharm doesn't appear to run all unit tests

I have a problem running unittests in pycharm. The first class 'KnownValues' runs but the other class doesn't get checked at all.
import roman
import unittest
class KnownValues(unittest.TestCase):
def test_too_large(self):
'''to_roman should fail with large input'''
self.assertRaises(roman.OutOfRangeError, roman.to_roman, 4000)
def test_too_small(self):
ls = [0,-1,-25,-60]
for x in ls:
self.assertRaises(roman.OutOfRangeError, roman.to_roman, x)
def test_non_int(self):
ls = [1.5, -6.5, 6.8,12.9, "hello wold", "nigga123"]
for x in ls:
self.assertRaises(roman.TypeError, roman.to_roman, x)
class Test2(unittest.TestCase):
def test1(self):
assert 1 == 1
if __name__ == '__main__':
unittest.main()
Start all of your test functions with test. Many people use underscores to separate words, so a lot of people end up with tests starting with test_, but test is all that is required.
When having trouble in the GUI, you can check how your tests are running from the command line.
python test.py
or
python -m test
One problem that you might run into is that you have defined your tests within classes, and when running them through the GUI, the GUI has automatically discovered them for you. Be sure to include the lines at the end of your test file directing the interpreter to use the main function built into unittest.
if __name__ == '__main__':
unittest.main()
Keep in mind, you can optionally run the tests in only one of your classes at a time:
python tests.py KnownValues
python tests.py Test2
In PyCharm, it should automatically discover all the test classes. You still have the option of running only one class at a time. Choose Run->Edit Configurations to see the options that you are currently running under. Using command line parameters you can control running fewer or more tests.
As you can see, you can choose to run a script, a class, or a method. Be sure to set the name of your run configuration such that it reflects the scope of what you are running.

unittest.py doesn't play well with trace.py - why?

Wow. I found out tonight that Python unit tests written using the unittest module don't play well with coverage analysis under the trace module. Here's the simplest possible unit test, in foobar.py:
import unittest
class Tester(unittest.TestCase):
def test_true(self):
self.assertTrue(True)
if __name__ == "__main__":
unittest.main()
If I run this with python foobar.py, I get this output:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Great. Now I want to perform coverage testing as well, so I run it again with python -m trace --count -C . foobar.py, but now I get this:
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
No, Python, it's not OK - you didn't run my test! It seems as though running in the context of trace somehow gums up unittest's test detection mechanism. Here's the (insane) solution I came up with:
import unittest
class Tester(unittest.TestCase):
def test_true(self):
self.assertTrue(True)
class Insane(object):
pass
if __name__ == "__main__":
module = Insane()
for k, v in locals().items():
setattr(module, k, v)
unittest.main(module)
This is basically a workaround that reifies the abstract, unnameable name of the top-level module by faking up a copy of it. I can then pass that name to unittest.main() so as to sidestep whatever effect trace has on it. No need to show you the output; it looks just like the successful example above.
So, I have two questions:
What is going on here? Why does trace screw things up for unittest?
Is there an easier and/or less insane way to get around this problem?
A simpler workaround is to pass the name of the module explicitly to unittest.main:
import unittest
class Tester(unittest.TestCase):
def test_true(self):
self.assertTrue(True)
if __name__ == "__main__":
unittest.main(module='foobar')
trace messes up test discovery in unittest because of how trace loads the module it is running. trace reads the module source code, compiles it, and executes it in a context with a __name__ global set to '__main__'. This is enough to make most modules behave as if they were called as the main module, but doesn't actually change the module which is registered as __main__ in the Python interpreter. When unittest asks for the __main__ module to scan for test cases, it actually gets the trace module called from the command line, which of course doesn't contain the unit tests.
coverage.py takes a different approach of actually replacing which module is called __main__ in sys.modules.
I don't know why trace doesn't work properly, but coverage.py does:
$ coverage run foobar.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
$ coverage report
Name Stmts Miss Cover
----------------------------
foobar 6 0 100%
I like Theran's answer but there were some catches with it, on Python 3.6 at least:
if I ran foobar.py that went fine, but if I ran foobar.py Sometestclass, to execute only Sometestclass, trace did not pick that up and ran all tests anyway.
My workaround was to specify defaultTest, when appropriate:
remember that unittest usually are run as
python foobar.py <-flags and options> <TestClass.testmethod> so targeted test is always the last arg, unless it's a unittest option, in which case it starts with -. or it's the foobar.py file itself.
lastarg = sys.argv[-1]
#not a flag, not foobar.py either...
if not lastarg.startswith("-") and not lastarg.endswith(".py"):
defaultTest = lastarg
else:
defaultTest = None
unittest.main(module=os.path.splitext(os.path.basename(__file__))[0], defaultTest=defaultTest)
anyway, now trace only executes the desired tests, or all of them if I don't specify otherwise.

Categories