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?
Related
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.
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
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.
I'm trying to unit test a class which is derived from a base_class in an external module. In my dev/test environment I have not access to this external module, which means I have to somehow mock this base_class.
My test-code resides in a different file from the code I'm trying to test.
The problem can be summarized as follows:
my_class.py
import external_module
class MyClass(external_module.ExternalClass):
def test_method(self):
return "successful"
test_my_class.py
import sys
import unittest
from unittest.mock import MagicMock
sys.modules['external_module'] = MagicMock()
from my_class import MyClass
class TestMyClass(unittest.TestCase):
def test_first(self):
my_class = MyClass()
result = my_class.test_method()
self.assertEqual(result, "successful")
if __name__ == '__main__':
unittest.main()
Results
When running test_my_class.py the result are the following.
AssertionError: <MagicMock name='mock.ExternalClass.test_method()' id='140272215184664'> != 'successful'
Clearly since the external_module is mocked, even MyClass becomes an instance of a mock-object.
Similar posts
The problem is similar to as described in Python mock: mocking base class for inheritance, but has the difference that the base_class is from an external module.
Even How to mock a base class with python mock library show som similarities to my problem, though the solutions can not be directly applied.
Tries and failures
To get the import
import external_module
to work in my_class.py
sys.modules['external_module'] = MagicMock()
need to be set in test_my_class.py.
Though, this leads to that external_module.* becomes a Mock-instance.
You could create a helper module mocked_external_module, which can be imported from your tests and also contains a class base_class. Then you do the following in your test code:
import mocked_external_module
sys.modules['external_module'] = mocked_external_module
Plus, every method of your base_class that you need to mock you can create as a Mock or MagicMock.
I'm trying to test some code using pytest and need to change a function from some module. One of my imports also imports that function, but this is failing when I change the method using monkeypatch. Here is what I have:
util.py
def foo():
raise ConnectionError # simulate an error
return 'bar'
something.py
from proj import util
need_this = util.foo()
print(need_this)
test_this.py
import pytest
#pytest.fixture(autouse=True)
def fix_foo(monkeypatch):
monkeypatch.setattr('proj.something.util.foo', lambda: 'bar')
import proj.something
And this raises ConnectionError. If I change
test_this.py
import pytest
#pytest.fixture(autouse=True)
def fix_foo(monkeypatch):
monkeypatch.setattr('proj.something.util.foo', lambda: 'bar')
def test_test():
import proj.something
Then it imports with the monkeypatch working as expected. I've read though this and tried to model my testing from it, but that isn't working unless I import inside of a test. Why does the monkeypatch not do anything if it is just a normal import in the testing file?
That is because the fixture is applied to the test function not the entire code. autouse=True attribute just says that it should be used in every test