test object can't see libraries called by script under test - python

I'm testing a script that mainly consists of an object Foo, which imports a library bar.
import bar
class Foo():
*do stuff*
f = Foo()
*do stuff*
Now I create a unittest class to test Foo.
import foo
class FooTest(unittest.TestCase):
def setUp(self):
self.foo = foo.Foo()
*do tests*
Later, it has a line like:
bar.method(...)
This fails with 'bar not defined', but shouldn't it be imported via import foo? How do automatically load whatever is required by the script under test?

The importer model indeed imports everything from the other file, but under the imported model name as a namespace. See an example:
so_imported.py
import json
class Test:
def __init__(self) -> None:
self.text = "A phrase"
so_import.py
import so_imported
class Test2:
def __init__(self) -> None:
self.inner = {"test": so_imported.Test().text}
print(so_imported.json.dumps(self.inner))
Test2()
So you see the json library imported under the module's namespace ?
If you want to import everything from the imported file without any namespaces, you can use: from foo import *. Then the objects will be merged into the importer namespace.

Related

Mock not seen by pytest

I got a class that describes an entity and am trying to write unit tests that should check if certain fields are defaulted to the correct value. One of those fields uses datetime.now() to set the default state.
I am having trouble trying to mock this call to now() in my test. I am guessing it has to do with my folder structure.
src
classes
MyClass.py
pytests
test_MyClass.py
The MyClass definition:
from datetime import datetime
class MyClass:
def __init__(self):
self.mom = datetime.now()
I am using #mock.patch as follows (inside test_MyClass.py):
import pytest
import sys
from unittest import mock
sys.path.append(r'C:\python-projects\test\src')
from classes.MyClass import MyClass
#mock.patch('classes.MyClass.datetime.now', return_value = 'timestamp', autospec = True)
#pytest.fixture
def myclass():
myclass = MyClass()
yield myclass
def test_has_datetime(myclass):
assert myclass.mom == 'timestamp'
The test ignores the patch.
Try this:
make sure you have __init__.py files in src, classesand pytests directories;
remove sys.path.append(r'C:\python-projects\test\src')
in #mock.patch, replace classes.MyClass.datetime.now with src.classes.MyClass.datetime.now
make C:\python-projects\test\ the current working directory and run pytest ./pytests
Otherwise, mocking the datetime module can be easier with packages like freezegun: https://github.com/spulec/freezegun

Dynamically importing modules as global variables

I am trying to dynamically import modules and get it as global variable.
I am using maya 2020 python interpreter (Python 2.7)
I have a test module called "trigger_test_script.py" under "/home/arda.kutlu/Downloads/" folder.
When I dont import any custom class and run this:
###########################################################################[START]
import sys
import imp
class TestClass(object):
def __init__(self):
self.filePath = None
self.asName = None
def action(self):
exec("global %s" % self.asName, globals())
foo = "imp.load_source('%s', '/home/arda.kutlu/Downloads/trigger_test_script.py')" %self.asName
cmd = "{0}={1}".format(self.asName, foo)
exec(cmd, globals())
###########################################################################[END]
test = TestClass()
test.filePath = "/home/arda.kutlu/Downloads/trigger_test_script.py"
test.asName = "supposed_to_be_global"
test.action()
print(supposed_to_be_global)
I get the exact result that I want:
<module 'trigger_test_script' from '/home/arda.kutlu/Downloads/trigger_test_script.pyc'>
However, when I save TestClass (the part between hashes) into a file and import it like this:
import testClass
test = testClass.TestClass()
test.filePath = "/home/arda.kutlu/Downloads/trigger_test_script.py"
test.asName = "supposed_to_be_global"
test.action()
print(supposed_to_be_global)
the variable which 'supposed_to_be_global' is not becoming global and I get the NameError.
I always assumed that these two usages should return the same result but clearly I am missing something.
I appreciate any help, thanks.
I don't quite understand your last comment about having several modules with different action() methods is a problem. So ignoring that, here's how to make what's in your question work, The part in the hashes will work both in-line or if put in a separate module and imported.
###########################################################################[START]
import imp
class TestClass(object):
def __init__(self):
self.filePath = None
self.asName = None
def action(self):
foo = imp.load_source(self.asName, self.filePath)
return foo
###########################################################################[END]
#from testclass import TestClass
test = TestClass()
test.filePath = "/home/arda.kutlu/Downloads/trigger_test_script.py"
test.asName = "supposed_to_be_global"
supposed_to_be_global = test.action()
print(supposed_to_be_global)

Avoid duplicate name when importing a sub-module with only one public member from a package

Basic Setup
Suppose I want to create a class named Foo. I may create a file like so:
foo.py:
class Foo:
def __init__(self):
self.data = "world"
def print(self):
print("Hello, " + self.data)
To utilize this class in my main script:
main.py
import foo
test = foo.Foo()
test.print()
Having to type foo.Foo() every time I instantiate the class already feels ridiculous enough, but it gets worse when I want to organize my code by separating my classes into a package:
classes/__init__.py
# Empty
classes/foo.py
# Copy of foo.py, above
main.py
import classes.foo
test = classes.foo.Foo()
test.print()
Simple Answer
I know I can clean this up somewhat by using from X import Y like so:
from classes.foo import Foo
test = Foo()
Preferred Answer
Because the file foo.py contains only one member whose name matches the file, I would prefer if I could do something like the following:
from classes import Foo
# Or:
import classes.Foo as Foo
test = Foo()
Is there a way to do this? Maybe with some code in my __init__.py?
In classes/__init__.py, put:
from .foo import Foo
Now you can write from classes import Foo.

How do I override imports of other files for unit testing

I am currently attempting to write unit tests for my Main.py's main() function
Here is a simplified version of my Main.py:
from Configuration import Configuration # Configuration.py is a file in the same dir
def main():
try:
Configuration('settings.ini')
except:
sys.exit(1) # Test path1
sys.exit(0) # Test path2
if __name__ == '__main__':
main()
In my Unit Tests\MainUnitTests.py I want to import ..\Main.py and fake the Configuration class in such a way that I can hit Test path1 and Test path2
I found that i can assert sys.exit() with the following:
with self.assertRaises(SystemExit) as cm:
main()
self.assertEqual(cm.exception.code, 1)
but I am having trouble overriding the from Configuration import Configuration
Thoughts?
So far I have tried the following within Unit Tests\MainUnitTests.py:
class FakeFactory(object):
def __init__(self, *a):
pass
sys.modules['Configuration'] = __import__('FakeFactory')
class Configuration(FakeFactory):
pass
Another example for demonstration:
foo.py:
from bar import a,b
x = a()
class a(object):
def __init__(self):
self.q = 2
y = a()
print x.q, y.q # prints '1 2' as intended
b() # I want this to print 2 without modifying bar.py
bar.py:
class a(object):
def __init__(self):
self.q = 1
def b():
t = a()
print t.q
when you use the import
from bar import a
it import the name directly into the module, so monkeypatching bar won't help, you need to override a directly in the main file:
def fake_a():
print("OVERRIDEN!")
main.a = fake_a
Do know that unittest has helper functions for this in the mock subpackage, I believe you could do something like:
from unittest.mock import patch
...
with patch("main.a", fake_a) as mock_obj: #there are additional things you can do with the mock_obj
do_stuff()
This would work in your first example with configuration since the class that needs to be patched is not used in the global scope although foo uses bar.a as soon as it is loaded so you would need to patch it before even loading foo:
from unittest.mock import patch
...
with patch("bar.a", fake_a) as mock_obj: #there are additional things you can do with the mock_obj
import foo #now when it loads it will be loaded with the patched name
However in this case foo.a would not be reverted at the end of the with block because it can't be caught by unittest... I really hope your actual use case doesn't use the stuff to be patched at module level.

Python unittest - testing multiple files with the same name

I am attempting to test multiple functions with the same name and signature. (Student assignment submissions.) My setup currently looks like this:
TestCases/
__init__
testcase1.py
submission1/
func.py
submission2/
func.py
submission3/
func.py
Each func.py contains a function named foo. I would like to test each occurrence of foo with the same test suite. The standard unittest setup of
import unittest
from func import foo
class TestFoo(unittest.TestCase):
def test_empty(self):
self.assertTrue(foo(''))
...
if __name__ == '__main__':
unittest.main()
has (unsurprisingly) problems with the from func import foo line since func is not in the same folder, and I cannot seem to get it to pick up the proper import dynamically.
It struck me that I could take advantage of having already extended the base class and eliminate the need for the import by doing something like:
import unittest
class TestFoo(unittest.TestCase):
def __init__(self, foo):
self.foo = foo
def test_empty(self):
self.assertTrue(self.foo(''))
and in my test program instantiate a TestFoo object and pass it the function to test:
tf = TestFoo(foo)
but then I haven't been able to properly define and call the test suite.
Or is there an entirely different approach that would do what I need?

Categories

Resources