PyDev importing from secondary source folders - python

Summary: Using PyDev, I can't get one Python module (FooTest) to import another (FooMock) in the same (test) source folder.
I have two source folders and four Python modules, with the directory structure below. The minimal example code is at the end of the question. Although I have provided a lot of information, please don't let this intimidate you - it's a simple problem scenario.
PyDevProject/
src/ (source folder for prod code)
foo/
__init__.py
Bar.py
Foo.py
test/ (source folder for tests)
foo/
__init__.py
FooMock.py
FooTest.py
The issue here is that when I attempt to import FooMock class in the FooTest module, I get the following error (running PyUnit to run the FooTest tests):
Finding files... done.
Importing test modules ... Traceback (most recent call last):
File "/Applications/Eclipse.app/Contents/Eclipse/plugins/org.python.pydev_4.5.4.201601292234/pysrc/_pydev_runfiles/pydev_runfiles.py", line 468, in __get_module_from_str
mod = __import__(modname)
File "/Users/me/Development/krtiWorkspace/ImportTest/test/foo/FooTest.py", line 4, in <module>
from foo.FooMock import FooMock
ImportError: No module named FooMock
ERROR: Module: FooTest could not be imported (file: /Users/me/Development/krtiWorkspace/ImportTest/test/foo/FooTest.py).
done.
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Things I've tried:
Even though FooMock and FooTest are both in the same Python package, FooMock can't be imported.
Moving FooMock to under the src/ directory works, even if placed under a unique Python package (as long as it's under src/). But this isn't acceptable for me (to have test code in production source folders).
I've ensured the PYTHONPATH settings in PyDev are set to specify both folders are source folders.
Copying and pasting the FooMock code into the FooTest code works. But this isn't acceptable for me (since I'd like to reference FooMock elsewhere, in multiple test files and I don't want multiple copies of FooMock floating around consequently).
Is there anything I can do about this?
Code:
# Code for Bar.py -----------------------------------------------------
class Bar(object):
def __init__(self, foo):
self.foo = foo
def next(self):
self.foo.incr()
# Code for Foo.py -----------------------------------------------------
class Foo(object):
def __init__(self):
self.num = 0
def incr(self):
self.num = self.num + 1
# Code for FooMock.py -------------------------------------------------
class FooMock(object):
def mock_incr(self):
self.hit = True
# Code for FooTest.py -------------------------------------------------
import unittest
from foo.Foo import Foo
from foo.Bar import Bar
from foo.FooMock import FooMock
class Test(unittest.TestCase):
def test_Foo(self):
foo = Foo()
foo.incr()
self.assertEquals(1, foo.num, "should be 1")
def test_Bar(self):
foo = FooMock()
bar = Bar(foo)
bar.next()
if __name__ == "__main__":
unittest.main()
Screenshot of directory:
Image showing directory structure and PYTHONPATH as previously explained

You have a module named foo in your PYTHONPATH twice. In the first directory, where the package foo is found, there is indeed no module named FooMock.
Can you rename the module foo under the test source directory to something else? Like footests instead of foo?

Related

VS Code doesn't recognize tests when import local class

I'm using Visual Studio Code and I want to use the panel Testing for my unittest tests. It's working for some but not all. Indeed when I import certains locals classes, VSCode doesn't recognize the test. If I remove the import the test is recognize.
Here is one of the class that I'm importing, I reduce it to the maximum:
class ConnectionAWS(InterfaceConnection):
def _connection(self) :
pass
def putFileInS3(self, file):
pass
Here the test :
import unittest
from repertoire.ConnectionAWS import ConnectionAWS
class TestConnectionAWS(unittest.TestCase):
def test_connection_success(self):
self.assertFalse(False)
def test_connection_failure(self):
self.assertFalse(False)
I didn't forget about init.py files.
Here the arborescence :
project
repertoire
ConnexionAWS.py
utils
Log.py
src
__init__.py
test
__init__.py
repertoire
__init__.py
test_connexionAWS.py
utils
__init__.py
test_log.py
test_log.py is recognized even if it's importing Log.py
Can someone explain to me where I'm wrong ?

pytest: I can run all tests from the root folder but I can't run tests individually (ImportError)

I can run all tests with pytest with no issues if I run from the test “root” directory, but if I try to run an individual test, I get an ImportError.
I have the following directory structure:
pytestExample/
conftest.py
src/
conftest.py
classA.py
config.py
tests/
test_classA.py
Here is what each file contains:
conftest.py, src/conftest.py:
Both instances of conftest.py are empty files. I found that I had to place conftest.py in the code root directory (pytestExample/) and the src/ directory for pytest to find the tests in test_classA.py.
tests/test_classA.py:
import unittest
from src.classA import ClassA
import src.config
import pytest
class ClassATest(unittest.TestCase):
def setUp(self):
self.classA = ClassA()
def test_checkVarAValue(self):
self.assertEqual(12345, self.classA.getVarA())
def test_setAndCheckVarA(self):
self.classA.setVarA(9876)
self.assertEqual(9876, self.classA.getVarA())
src/classA.py:
import config
class ClassA:
varA = 0
def __init__(self):
self.varA = config.DEFAULT_VAR_A
def getVarA(self):
return self.varA
def setVarA(self, value):
self.varA = value
src/config.py:
DEFAULT_VAR_A = 12345
If I run pytest from pyTestExample\, all tests run fine and pass:
Running from code root directory
However, if I try to run a test individually, I get an ImportError:
Running a test individually
I also see the same error if I just try to run the test directory:
Running test directory
Any ideas on what could be wrong here?

How to properly mock a function that is imported in a package?

I try to test a package that I created and for that I need to mock subprocess.Popen. I want to do this with the patch decorator but I can't specify the right target. I keep getting errors that the attribute doesn't exists in the class.
The structure of my project is like this:
.
|- package
| |- __init__.py
| |- package.py
|- tests
| |- test_script.py
__init__.py
from package.package import package
package.py
from subprocess import Popen
class package:
def __init__(self):
self.foo = "bar"
def echo_cmd(self):
p = Popen("test.bat", stdout=subprocess.PIPE)
p.wait()
x = p.communicate()
return x
test_script.py
import unittest
from unittest.mock import patch, Mock
import package
class TestADBHelper(unittest.TestCase):
def setUp(self):
self.test = package.package()
#patch("package.Popen")
def test_connect_to_device_not_connected(self, mock_popen):
"""test if a device can be connected"""
process = mock_popen.return_value
process.returncode = 0
process.communicate.return_value = "foo"
I want to mock the output of Popen. I tried different ways of importing and defining the target of the mock but I keep getting errors like:
AttributeError: <module 'package' from 'C:\\Users\\***\\Documents\\mock_test\\package\\__init__.py'> does not have the attribute 'Popen'
You have two things called package and you're mocking the wrong one.
#patch("package.package.Popen")
I figured it out :-) It all became clear when I change the names of all the files to package1, package2 and package3. Then the answer above works as expected:
#patch("package1.package2.Popen")
Somehow Python always imports the class if the name of the file is the same as the class. So that was the reason it was not able to find "Popen"

Python unittest with TestLoader results in ModuleNotFoundError

i try to run a unittest with unittest.TestLoader and unittest.TextTestRunner but get an ModuleNotFoundError everytime i try to run the 'main' test file (here: test_all.py). I have the following file structure:
src.
test_all.py
dir1.
__init__.py
module1.py
submodule1.py
test_module1.py
dir2.
__init__.py
module2.py
submodule2.py
test_module2.py
dir3.
...
The test_all.py file looks like this:
# test_all.py
import os
import unittest
loader = unittest.TestLoader()
suite = loader.discover(os.getcwd())
runner = unittest.TextTestRunner()
runner.run(suite)
And finally the structure of the single testcases look like this:
# test_module1.py
import unittest
from module1 import Module1
class Module1TestCase(unittest.TestCase):
def setUp(self):
# do something
def test_something(self):
# test test
def tearDown(self):
# do something
if __name__ == '__main__':
unittest.main()
So, running the test_all.py always results in an ModuleNotFoundError referencing to the from module1 import Module1 inside the TestCase test_module1.py (and the same in the following TestCases). As far as i can tell there are no circular dependencies. Maybe adding the current Path to the PythonPath would work, but it really makes no sense to me: on the one hand i run the test_all.pyas main in the current directory and on the other the unittest.TestLoader.discover() already takes the current path.
PS: I know that putting all the TestCases in one folder is way better. But first i want to figure out why this is not working. Thanks!

Using class from another file in python package and conflicts in __init__()

I'm writing a python package (python 3.6) and have the following directory structure:
package/
| __init__.py
| fileA.py
| fileB.py
| tests/
| | __init__.py
| | test_classA.py
| | test_classB.py
Setup
My files have the following contents:
# package/fileA.py
from package import ClassB
def ClassA:
def __init__(self):
self.my_ClassB = ClassB()
-
# package/fileB.py
def ClassB:
def __init__(self):
self.foo = "bar"
-
# package/tests/test_classB.py
from package import ClassB
# <performs some unit tests here>
-
# package/tests/test_classA.py
from package import ClassA
# <performs some unit tests here>
-
# package/__init__.py
from .fileA import ClassA
from .fileB import ClassB
Circular importing
When I ran python test_classB.py, I get the following traceback error showing that I have circular import statements, which are not allowed by python. Note - the package is not literally called package and I have edited the Traceback to match the toy example above.
Traceback (most recent call last):
File "package/tests/test_classB.py", line 2, in <module>
from package import ClassB
File "/anaconda/lib/python3.5/site-packages/package/__init__.py", line 2, in <module>
from .fileA import ClassA
File "/anaconda/lib/python3.5/site-packages/package/merparse.py", line 2, in <module>
from package import ClassB
ImportError: cannot import name 'ClassB'
Correcting the error
However, when I remove those two lines in my package/__init__.py file:
# package/__init__.py
from .fileA import ClassA
from .fileB import ClassB
...and I change the import method for package/fileA.py:
# package/fileA.py
from package.fileB import ClassB
def ClassA:
def __init__(self):
self.my_ClassB = ClassB()
... package/tests/test_classB.py runs correctly.
My Question
My question is: How can I keep the one file: one class structure in my files and import with from package import ClassA instead of having to import with from package.fileA import ClassA?
In my package I would like to import classes from other files, but don't know how to get around circular importing.
Edit: Solution
Thanks to #mirandak and #martin-kalcok below for their help.
The only file that I had to edit was fileA.py to not refer to the package name in the import statement.
# package/fileA.py
from .fileB import ClassB
def ClassA:
def __init__(self):
self.my_ClassB = ClassB()
The package/__init__.py file still contains import statements in case I want to import the package from other scripts in the future that I don't couple with the package.
# package/__init__.py
from .fileA import ClassA
from .fileB import ClassB
The problem is with package/fileA.py. It's both a part of package and calling the __init__.py file of package as it's imported - creating a circular dependency.
Can you change fileA.py to look like this?
# package/fileA.py
from .fileB import ClassB
def ClassA:
def __init__(self):
self.my_ClassB = ClassB()
First of all, renaming your package to literary "package" really confuses this whole situation, but here's my take on it :D.
I don't think you are dealing with circular dependency, python just states that it cannot import classB from package and that is because there is no file named classB in package directory. Changing import statement to
from package.fileB import ClassB
works because there is directory package which contains file fileB which contains object ClassB.
To answer your question whether you can import classes with statement
from package import ClassB
you can't, unless your package is file and classB is object inside this file. However i don't see how this different import statement would be a dealbraker
Side Note: Your objects ClassB and ClassA are not classes, they are functions. Python uses keyword class to create classes and def to define functions.
Side note 2: Are you sure your __init__.py file needs to hold any code at all?
Personal Note: I know that keeping 1 File / 1 Class structure is matter of personal preferences, it is not required in python and I myself find it much more comfortable to group multiple related classes and functions into one file

Categories