I have a unittest that tests the connection of an url. Individually it works, but I have several urls to test, so I'm trying to call this test module and batch-test them! But I get errors in calling the test function. Could you help me?
test.py:
class TestConnector(unittest.TestCase):
def setUp(self):
[...]
def test_connection(self, url):
conn = Connector(self.user)
self.assertNotEqual(conn.read(url), None)
if __name__ == '__main__':
unittest.main()
Now I want to test several urls, so I created file with them, and try to call the test function:
import test
with open('URL_list.txt') as f:
urls = f.readlines()
suite = unittest.TestLoader().loadTestsFromModule(test.TestConnector)
for url in urls:
unittest.TextTestRunner().run(suite)
And I get this message as many times as urls I have:
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
----------------------------------------------------------------------
What's wrong?
Your test method is ignored because it takes an argument. Test methods never take an argument. This is quite beside the fact that Python won't ever magically pass a local variable name into a function as an argument; you'd have to explicitly pass it in.
Integrate your url loading into the test itself instead:
class TestConnector(unittest.TestCase):
def setUp(self):
[...]
def test_connections(self):
with open('URL_list.txt') as f:
for url in f:
conn = Connector(self.user)
self.assertNotEqual(conn.read(url.strip()), None)
if __name__ == '__main__':
unittest.main()
or create test methods dynamically:
class TestConnector(unittest.TestCase):
def setUp(self):
[...]
def generate_test(url):
def test(self):
conn = Connector(self.user)
self.assertNotEqual(conn.read(url), None)
if __name__ == '__main__':
with open('URL_list.txt') as f:
for i, url in enumerate(f):
test_name = 'test_{}'.format(i)
setattr(TestConnector, test_name, generate_test(url.strip()))
unittest.main()
Related
I have this class in a file called local.py:
def get_credentials(creds):
data = creds
return data
class ClassA:
def __init__(self):
body = "not a json"
self.credentials = get_credentials(body)
def run(self):
print(self.credentials)
def main():
inst = ClassA()
inst.run()
if __name__ == "__main__":
main()
all it does is return the credentials passed to it.
I want to mock the get_credentials function for which I have another file test_local.py:
from local import ClassA
import unittest
from unittest.mock import patch
def mock_get_credentials(creds):
return "123"
class NameTestCase(unittest.TestCase):
patch('local.get_credessntials', new=mock_get_credentials("test"))
def test_whatever(self):
print("true")
inst = ClassA()
inst.run()
self.assertEqual(1, 1)
if __name__ == '__main__':
unittest.main()
The output I keep getting is this:
true
not a json
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
The fact that it spits out "not a json" to me shows that it is not taking in the mocked value. I don't understand why it's doing this as I feel I have follow the documentation. Would appreciate some help with this as to why its not getting mocked.
There are a few typos in your code: you forgot the # for the patch decorator, the patched name is wrong, and you pass the function result instead of the function to new. Here is the relevant part of the fixed test:
class NameTestCase(unittest.TestCase):
#patch('local.get_credentials', new=mock_get_credentials)
def test_whatever(self):
inst = ClassA()
inst.run()
self.assertEqual(1, 1)
Ok please look at my example code
My ConnectBase class:
class ConnectBase(unittest.TestCase):
def setUp(self):
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['deviceName'] = '4cfe4b59'
desired_caps['platformVersion'] = '5.0'
desired_caps['appPackage'] = 'com.xyz.bookshelf'
desired_caps['appActivity'] = 'com.xyz.bookshelf.MainActivity'
desired_caps['noReset'] = False
self.driver_android = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
self.driver_android.implicitly_wait(30)
And my main file with tests:
import unittest
from connect import ConnectBase
from main_screen import MainScreenCheck
class MainTests(ConnectBase, MainScreenCheck):
with open('report.txt', 'w') as f:
f.write("----------Bookshelf----------\n")
def test_bookshelf_tutorial(self):
self.addToFile("Test Tutorial")
self.driver_android.orientation = 'LANDSCAPE'
super(MainTests, self).logout_screen_check()
def test_bookshelf_2(self):
self.addToFile("Test 2")
super(MainTests, self).login_screen_check()
def test_bookshelf_3(self):
self.addToFile("Test 3")
super(MainTests, self).loading_screen_check()
def test_bookshelf_4(self):
self.addToFile("Test 4")
super(MainTests, self).list_check()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(MainTests)
unittest.TextTestRunner(verbosity=2).run(suite)
I run script -> it is connecting
It starts "test_bookshelf_tutorial()"
Test passed and i would like to continue with "test_bookshelf_2()" but the app is restarting... and i have to go throught tutorial screen again...
The problem is that every unittest "def test_xyz(self)" application is restarting so I can't use unittest function that shows the passed test in report becouse, each test I must go through everything that I made in tests before
I created my way to make a test report -> I'm adding each test result to txt file... but I wonder if there is a possibility to turn off this app restarting and use normal unittest reports?
Or maybe there is another great way to do reports of automation tests?
Try to make an order to your test cases, some times test depend from each other
in the first step open the application and close it only in the last step:
class MainTests(ConnectBase, TestCase):
def step1(self):
#open the application
def step2(self):
...
def steps(self):
for name in sorted(dir(self)):
if name.startswith("step"):
yield name, getattr(self, name)
def test_steps(self):
for name, step in self.steps():
try:
step()
except Exception as e:
self.fail("{} failed ({}: {})".format(step, type(e), e)
I suggest that you use a Test framework like 'TESTNG' to define test priority to manage tests order but be sure that the first test is always executed to open the application ;)
Currently I have a function that creates a screenshot and I call it here
def tearDown(self):
self.save_screenshot()
self.driver.quit()
There is also a folder being created which is used to store the screenshots.
I don't want this to happen when the test passes.
What do I have to add in order for this not to happen?
Thanks for all the help
If your test failed, the sys.exc_info will have an exception. So you can use it as pass/fail result of your test:
if sys.exc_info()[0]:
# 'Test Failed'
else:
# 'Test Passed'
And if you want to take a screenshot on failure:
import unittest
import sys
from selenium import webdriver
class UrlTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
def test_correct_url(self):
self.driver.get('https://google.com')
self.assertTrue('something.com' in self.driver.current_url)
def tearDown(self):
if sys.exc_info()[0]:
self.driver.get_screenshot_as_file('screenshot.png')
self.driver.quit
if __name__ == '__main__':
unittest.main()
Here is one way to capture screenshot only when failure:
def setUp(self):
# Assume test will fail
self.test_failed = True
def tearDown(self):
if self.test_failed:
self.save_screenshot()
def test_something(self):
# do some tests
# Last line of the test:
self.test_failed = False
The rationale behind this approach is when the test reaches the last line, we know that the test passed (e.g. all the self.assert* passed). At this point, we reset the test_failed member, which was set to True in the setUp. In tearDown, we now can tell if a test passed or failed and take screenshot when appropriate.
In your initialisation method set a self.NoFailuresSnapped = 0 and check your test environment for the current number of failures being > self.NoFailuresSnapped before calling or within your function and of course set it again before returning.
I know it is a bit silly question, but using links provides below, I am still unable to create testsuite.
I have now two test cases (there will be much more), let assume that the name of there are:
class step1(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
def test_case1(self):
[...]
if __name__ == "__main__":
unittest.main()
and:
class step2(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
def test_case2(self):
[...]
if __name__ == "__main__":
unittest.main()
I want to create other file .py file: testsuite, which can aggregate test_case1, test_case2, test_case3...
I tried something like that, for example:
import unittest
import step1
import step2
def suite():
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.step1(test_case1))
test_suite.addTest(unittest.step2(test_case2))
if __name__ == "__main__":
result = unittest.TextTestRunner(verbosity=2).run(suite())
sys.exit(not result.wasSuccessful())
Error: AttributeError: 'module' object has no attribute 'step1'
You can use addTest() and pass TestCase instance to it, you also miss return statement:
def suite():
test_suite = unittest.TestSuite()
test_suite.addTest(step1())
test_suite.addTest(step2())
return test_suite
or, in one line using addTests():
test_suite.addTests([step1(), step2()])
I want to write tests for my main file,calc.py, with unittest in module file, MyTests.py.
Here is my main python file, calc.py:
import myTests
def first(x):
return x**2
def second(x):
return x**3
def main():
one = first(5)
two = second(5)
if __name__ == "__main__":
main()
try:
myTests.unittest.main()
except SystemExit:
pass
And here is my MyTests.py file:
import unittest
import calc
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.testInput = 10
def test_first(self):
output = calc.first(self.testInput)
correct = 100
assert(output == correct)
def test_second(self):
output = calc.second(self.testInput)
correct = 1000
assert(output == correct)
When i run my calc.py, i get the following output:
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Why does unittest prints me that "Ran 0 test"?
And what is the right way to write unittest in module?
unittests.main() looks for TestCase instances in the current module. Your module has no such testcases; it only has a myTests global.
Best practice is to run the tests themselves. Add the __main__ section there to the myTests.py file:
import unittest
import calc
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.testInput = 10
def test_first(self):
output = calc.first(self.testInput)
correct = 100
assert(output == correct)
def test_second(self):
output = calc.second(self.testInput)
correct = 1000
assert(output == correct)
if __name__ == '__main__':
unittest.main()
and run python myTests.py instead.
Alternatively, pass in the imported myTests module into unittest.main(). You may want to move the import myTests line down into __main__, because you have a circular import too. That is fine in your case, myTests doesn't use any globals from calc outside of the test cases, but it is better to be explicit about this.
if __name__ == "__main__":
main()
try:
import myTests
myTests.unittest.main(myTests)
except SystemExit:
pass