I have a number of tests cases each defined in separate python files.
<file1.py>
class tc(testcase.tc):
....
<file2.py>
class tc(testcase.tc):
....
My main processing loop in RunTests.py has the following
class TestTable(wx.grid.PyGridTableBase):
def __init__(self):
wx.grid.PyGridTableBase.__init__(self)
self.cols = "Execute Number Name Status".split()
self.cases = []
package = tests
prefix = package.__name__ + "."
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, prefix):
if 'tests.testcase' != modname: # skip the testcase.py file/module
print modname
module = __import__(modname, fromlist="dummy")
print module.tc()
self.cases.append( module.tc() )
I end up getting AttributeError: 'module' object has no attribute tc. My prints show the test case and tc instance
test.xxx
Not sure why....
Thanks
Related
I have some class SomeClass in file prog.py
class SomeClass:
def __init__(self, file_name):
self.file_name = file_name
self.data_dict = {}
def load(self):
"""
"""
if not os.path.exists(self.file_name):
return False
with open(self.file_name, "r", encoding='iso-8859-1') as file:
skip_lines(4, file)
reader = csv.DictReader(file)
for line in reader:
if line['Region'] == "":#to remove Copiright and disclaimer text at the end
break
label=re.sub('\s+/\s+',"/",line['Region']) #remove spaces around /
if label in self.data_dict.keys():
#if line['Region'] in self.data_dict.keys():
self.data_dict[label][line['Range']] = int(line['All people'])
#self.data_dict[line['Region']][line['Range']] = line['All people']
else:
self.data_dict[label] = {line['Range']:int(line['All people'])}
#self.data_dict[line['Region']] = {line['Range']:line['All people']}
return True
def regions(self):
return list(self.data_dict.keys())
I need to:
Create a file named test_someclass_data.py.
Use import to import the prog module.
In the test_someclass_data.py file, create a unit test class named
TestSomeClass that inherits from unittest.TestCase.
The unit test class should include member functions to test the
regions member function of the SomeClass class.
My code of test_someclass_data.py:
import unittest
import prog
class TestSomeClass(unittest.TestCase):
def test_regions(self):
"""
Test regions function of the SomeClass class.
"""
prog.SomeClass.data_dict ={"key1":"val1","key2":"val2"}
test_output = prog.SomeClass.regions()
expected_output =["key1", "key2"]
self.assertEqual(test_output, expected_output)
I need to test a function of the SomeClass, so I have to create an instance of the class, call method call to store data, then I can compare outputs. Could you advise me, please, how to avoid this and test functions through mocking? Thank you.
Python Code:
class Importer:
from importlib import __import__, reload
from sys import modules
libname = ""
import_count = 0
module = None
def __init__(self, name):
self.libname = name
self.import_count = 0
def importm(self):
if self.libname not in self.modules:
self.module = __import__(self.libname)
else:
print("must reload")
self.module = self.reload(self.module)
self.import_count += 1
# test out Importer
importer = Importer("module")
importer.importm() # prints Hello
importer.importm() # prints Hello
importer.importm() # prints Hello (again)
print(importer.import_count)
The above Python (3.8.1) code is at OnlineGDB, which if you run, will give an error:
TypeError: reload() takes 1 positional argument but 2 were given
When I open up the importlib library in Python, I see this:
# ... (previous code; unnecessary)
_RELOADING = {}
def reload(module): ## this is where reload is defined (one argument)
"""Reload the module and return it.
The module must have been successfully imported before.
"""
if not module or not isinstance(module, types.ModuleType): ## check for type of module
raise TypeError("reload() argument must be a module")
try:
name = module.__spec__.name
except AttributeError:
name = module.__name__
if sys.modules.get(name) is not module: ## other code you (probably) don't have to care about
msg = "module {} not in sys.modules"
raise ImportError(msg.format(name), name=name)
if name in _RELOADING:
return _RELOADING[name]
_RELOADING[name] = module
try:
parent_name = name.rpartition('.')[0]
if parent_name:
try:
parent = sys.modules[parent_name]
except KeyError:
msg = "parent {!r} not in sys.modules"
raise ImportError(msg.format(parent_name),
name=parent_name) from None
else:
pkgpath = parent.__path__
else:
pkgpath = None
target = module
spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, target)
if spec is None:
raise ModuleNotFoundError(f"spec not found for the module {name!r}", name=name)
_bootstrap._exec(spec, module)
# The module may have replaced itself in sys.modules!
return sys.modules[name]
finally:
try:
del _RELOADING[name]
except KeyError:
pass
# ... (After code; unnecessary)
All double hashtag (##) comments are mine
It is clearly visible that reload DOES have 1 argument, and it checks if that argument is a module. In the OGDB (OnineGDB) code, I am only passing one argument (pretty sure) and it is of type module (most likely). If I remove that argument (you can edit the OGDB), it gives:
TypeError: reload() argument must be module
So for some reason, Python keeps thinking I have one more argument than I do actually have. The only way I made it work was editing the importlib file to have reload have two arguments (not a good idea).
I tried running PDB, not helpful.
Can anyone spot anything obviously wrong, like actually having two arguments?
What I needed to do is put the imports outside the class for it to work. Here is the new OGDB. Credits to #L3viathan. Code below:
from importlib import __import__, reload
from sys import modules
class Importer:
libname = ""
import_count = 0
module = None
def __init__(self, name):
self.libname = name
self.import_count = 0
def importm(self):
if self.libname not in modules:
self.module = __import__(self.libname)
else:
print("must reload")
self.module = reload(self.module)
self.import_count += 1
# test out Importer
importer = Importer("module")
importer.importm() # prints Hello
importer.importm() # prints Hello
importer.importm() # prints Hello (again)
print(importer.import_count)
You're having an issue because you're calling self.reload(self.module), which is actually equivalent to calling reload(self, self.module). To see this, try running the following:
class Example:
def some_method(*args):
print(args)
def some_other_method(self):
self.some_method(1)
an_example = Example()
example.some_other_method()
You should see that this prints out 2 arguments, not 1, (the first of which is a reference to self) despite us only passing one argument to some_method, and some_method having no self argument.
It would be better to import the reload method within your importm method (or outside the class altogether!), like so:
def importm(self):
from importlib import __import__, reload
if self.libname not in self.modules:
self.module = __import__(self.libname)
else:
print("must reload")
self.module = reload(self.module)
self.import_count += 1
import mymodule
reload(mymodule)
is how it would work ... Im not sure what your question is from that big wall of text above this is typically used to reset state to its initial state
mymodule.py
x = 5
main.py
from importlib import reload # in py2 you did not need to import it
import mymodule
print(mymodule.x) # 5
mymodule.x = 8
print(mymodule.x) # 8
reload(mymodule)
print(mymodule.x) # 5 again
I have developed a Tkinter application which will basically display the test functions inside a test file and the user can select the particular test functions and run pytest on it. It was working well so far as I had only test functions and no classes. Now, there are classes and functions inside it. How do I capture that those functions come inside that particular class? I thought of using regex but there might be functions outside the class too. So, I dont know how to solve this issue.
So far I have something like this:
Test file:
def test_x():
....
def test_y():
....
Source Code:
with open("{}.py".format(testFile), "r") as fp:
line = fp.readline()
while line:
line = fp.readline()
if ("#" not in line) and ("def" and "test_" in line):
x = line.split()[1].split('(')[0]
gFunctionList.append([testName, x])
Based on which all selected:
#var2State is the checkbutton states
for j in range(len(var2State)):
if var2State[j].get() == 1:
runString += "{}.py::{} ".format(gFunctionList[j][0],
gFunctionList[j][1])
else:
continue
if runString != "":
res = os.system("pytest " + runString)
From the above code, it will run: pytest testFile.py::test_x if test_x is selected.
Now if the test file is like this:
Class test_Abc():
def test_x():
....
def test_y():
....
def test_j():
....
Class test_Xyz():
def k():
....
def test_l():
....
Class test_Rst():
def test_k():
....
def ltest_():
....
Now, if test_l is selected, it should run: pytest testFile.py::test_Xyz::test_l.
But how do I get the test_Xyz above?
if test_j is selected, it should run: pytest testFile.py::test_j.
So, how do I capture the class name right outside a particular set of test functions and not capture if it's not inside the class?
I am not really that familiar with Tkinter or how things get selected, but this might point you in the right direction.
As the link I provided in the comments indicates, instances (i.e. classes) do not have names. If you want to give an instance a name, you just need to include it in the def __init__(self):. When any method (i.e. function) from test_ABC (which also inherits self) is called, you will always have access to self.name.
class test_Abc():
def __init__(self):
self.name = 'test_Abc'
def test_x(self):
return self.name
def test_y(self):
return self.name
abc = test_Abc()
a = abc.test_x()
print('pytest testFile.py::' + a + '::test_x')
which returns:
pytest testFile.py::test_Abc::test_x
I try to get the list of class from python file using python. After a few search, I get the code which I think it's work as follow
def get_class_from_file(class_obj, file, path='app', exclude=[]):
class_list = []
module = importlib.import_module(path + '.' + file)
for x in dir(module) :
app_cls = getattr( importlib.import_module(path + '.' + file), x )
try :
if app_cls and issubclass(app_cls, class_obj) and app_cls != class_obj and app_cls not in exclude:
class_list.append( (file, x) )
except TypeError :
pass
return class_list
However, I found out that the code don't get only the list of the class, but It still keep showing me the superclass of the class inside the file, here is example
file_1.py
class A:
pass
class B(A):
pass
file_2.py
class C(B):
pass
class D:
pass
when I call the function as
class_list = get_class_from_file(A, 'file_2')
I expect the result would be [C], but It return [C, B] as B is one of super class of C
Please help me fix this, I just want class inside the given file, not any superclass of them. By the way, I use exclude for fixing it at first, but It isn't give me a long run solution.
The problem is that imported modules are also found. You can check a class'
__module__ attribute to see if it originates from the current module or was imported into it.
You also have importlib.import_module(path + '.' + file) twice, I removed one of them. I renamed x to name.
def get_class_from_file(class_obj, file, path='app', exclude=[]):
class_list = []
module_path = path + '.' + file
module = importlib.import_module(module_path)
for name in dir(module) :
app_cls = getattr(module, name)
try:
if (issubclass(app_cls, class_obj) and
app_cls != class_obj and
app_cls not in exclude and
app_cls.__module__ == module_path):
class_list.append( (file, name) )
except TypeError:
# Not a class
pass
return class_list
In order to run all my unittests I use following script where test_files is a list of strings of my testfiles:
for test_file in test_files:
test_file = test_file[:-3]
module = __import__(test_file)
for name, obj in inspect.getmembers(module):
if inspect.isclass(obj):
if str(obj).startswith("<class 'test_"):
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(obj))
How can I remove single tests from the suite afterwards (not all tests from a testfile)?
I finally ended up creating a new suite and added all tests except the ones I want to skip. In order for the test to be listed as skipped I created a dummy SkipCase class.
class SkipCase(unittest.TestCase):
def runTest(self):
raise unittest.SkipTest("Test would take to long.")
new_suite = unittest.TestSuite()
blacklist = [
'test_some_test_that_should_be_skipped',
'test_another_test_that_should_be_skipped'
]
for test_group in suite._tests:
for test in test_group:
if test._testMethodName in blacklist:
testName = test._testMethodName
setattr(test, testName, getattr(SkipCase(), 'runTest'))
new_suite.addTest(test)
You can use this skip decorator on a class or method basis:
import unittest
#unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
class MarketTest(unittest.TestCase):
def setUp(self):
return
#unittest.skip("Skipping market basics test")
def test_market_libraries(self):
return