I'm having an issue when trying to write unit tests for a python3 script. The code is throwing a AttributeError while running inside a test definition, but does not if I run the same code in the same file outside of the test definitions class.
The code:
class TestDataCleaningMethods(unittest.TestCase):
def test_one(self):
data = pd.DataFrame(columns=[
'categoryId',
'regionId',
'districtId',
'jobType',
'contractType',
'payType'])
result = service.__columns_to_snake_case(data)
Throws the following error:
AttributeError: module 'service' has no attribute '_TestDataCleaningMethods__columns_to_snake_case'
I've had a search around, and found a few questions and answers regarding attribute errors on unittest, but haven't seen any that quite match this issue.
Another thing to note is that I've Monkey-patched import to return Mocks, but I can't see it interfering with the functioning of unittest. In the interest of completedness, this is that code:
orig_import = __import__
original_imports = [
"service",
"pandas"
]
def import_mock(name, *args):
if name in original_imports:
print(f"Using original imports for {name}")
return orig_import(name, *args)
print(f"Mocking {name}")
return mock.MagicMock()
with mock.patch('builtins.__import__', side_effect=import_mock):
import service
Any help is greatly appreciated
Solved it, turns out it is an issue with double underscores in function names. Double underscore makes the python interpreter to rename the attribute to prevent conflicts. Changing the function names to remove the double underscore fixes the issue.
Related
For context, I am using the Python ctypes library to interface with a C library. It isn't necessary to be familiar with C or ctypes to answer this question however. All of this is taking place in the context of a python module I am creating.
In short, my question is: how can I allow Python linters (e.g. PyCharm or plugin for neovim) to lint objects that are created at runtime? "You can't" is not an answer ;). Of course there is always a way, with scripting and the like. I want to know what I would be looking at for the easiest way.
First I introduce my problem and the current approach I am taking. Second, I will describe what I want to do, and ask how.
Within this C library, a whole bunch of error codes are defined. I translated this information from the .h header file into a Python enum:
# CustomErrors.py
from enum import Enum
class CustomErrors(Enum):
ERROR_BROKEN = 1
ERROR_KAPUTT = 2
ERROR_BORKED = 3
Initially, my approach is to have a single exception class containing a type field which described the specific error:
# CustomException.py
from CustomErrors import CustomErrors
class CustomException(Exception):
def __init__(self, customErr):
assert type(customErr) is CustomError
self.type = customErr
super().__init__()
Then, as needed I can raise CustomException(CustomErrors.ERROR_KAPUTT).
Now, what I want to do is create a separate exception class corresponding to each of the enum items in CustomErrors. I believe it is possible to create types at runtime with MyException = type('MyException', (Exception,), {'__doc__' : 'Docstring for ABC class.'}).
I can create the exception classes at runtime like so:
#CustomException.py
from CustomErrors import CustomErrors
...
for ce in CustomErrors:
n = ce.name
vars()[n] = type(n, (Exception,), {'__doc__' : 'Docstring for {0:s} class.'.format(n)})
Note: the reason I want to create these at runtime is to avoid hard-coding of an Exception list that change in the future. I already have the problem of extracting the C enum automatically on the backburner.
This is all well and good, but I have a problem: static analysis cannot resolve the names of these exceptions defined in CustomException. This means PyCharm and other editors for Python will not be able to automatically resolve the names of the exceptions as a suggested autocomplete list when the user types CustomException.. This is not acceptable, as this is code for the end user, who will need to access the exception names for use in try-except constructs.
Here is the only solution I have been able to think of: writing a script which generates the .py files containing the exception names. I can do this using bash. Maybe people will tell me this is really the only option. But I would like to know what other approaches are suggested for solving this problem. Thanks for reading.
You can add a comment to tell mypy to ignore dynamically defined attribute errors. Perhaps the linters that you use share a similar way to silence such errors.
mypy docs on silencing errors based on error codes
This example shows how to ignore an error about an imported name mypy thinks is undefined:
# 'foo' is defined in 'foolib', even though mypy can't see the
# definition.
from foolib import foo # type: ignore[attr-defined]
I have a small function as follows:
def write_snapshot_backup_monitoring_values():
try:
snapshot_backup_result = 'my result'
with open(config.MONITOR_SNAPSHOT_BACKUP_FILE, "w") as snapshot_backup_file:
snapshot_backup_file.write(snapshot_backup_result)
except Exception as exception:
LOG.exception(exception)
where config.MONITOR_SNAPSHOT_BACKUP_FILE is declared in a config file with value = /home/result.log
when I try to write a test case using pytest and I call this function as follows:
constants.MONITOR_SNAPSHOT_BACKUP_FILE = "/tmp/result.log"
#pytest.mark.functional_test
def test_write_snapshot_backup_monitoring_values():
utils.write_snapshot_backup_monitoring_values()...
I want to monkey patch the value for config.MONITOR_SNAPSHOT_BACKUP_FILE with constants.MONITOR_SNAPSHOT_BACKUP_FILE which I have declared in the test case file. Basically I want that while runnning the test case it should create /tmp/result.log and not /home/result.log How can I do that? I am new to monkey patching in python.
You don't clear up what config is, so I assume it is another module you have imported. There's no specific technique for monkey-patching, you just assign the value. It's just a name for adding/modifying attributes at runtime.
config.MONITOR_SNAPSHOT_BACKUP_FILE = constants.MONITOR_SNAPSHOT_BACKUP_FILE
However, there's one thing to keep in mind here: Python caches imported modules. If you change this value, it will change for other python modules that have imported config and run in the same runtime. So, be careful that you don't cause any side effects.
Im trying to run some tests in python. Im using Unittest framework.
The test "test_processJson" uses a test Json, dictTestString, and then checks if it has one or more elements. This is my script "testing.py"
import json
import starter#The code Im trying to test
import unittest
class MyTests(unittest.TestCase):
def test_processJson(json):
dictTestString = '{"city":"Barcelona"}'
jTest = json.loads(dictTestString)
dictProcess = starter.processJson(dictTest)
self.assertEquals(dictProcess["city"], "Barcelona")
if __name__ == '__main__':
unittest.main()
The problem comes when I run the test I get this error:
Traceback (most recent call last):
File "testing.py", line 16, in test_processJson
jTest = json.loads(dictTestString)
AttributeError: 'MyTests' object has no attribute 'loads'
I'm new to python, so I've been looking for an answer but any of the mistakes I've seen Im not doing.
Any help will be appreciated.
Thanks.
Your function's argument is named json, which shadow's the global json module. Actually since this is the first argument of a method, it get bound to the current MyTest instance, and since unittest test methods only expect the current instance as argument AND you don't have any need for a json argument here, you just have to rename it to self (which is the convention for the first argument of instance methods) and you problem will be solved.
NB : There are a couple other typos / issues with your code but I leave it up to you to find and solve them - that's part of the fun isn't it ?
So I have a module that I successfully use. But now I added another file to module and it gives me this error.
I have a file generate_bags_uk that has method:
def generate_bags(bags, price):
And I use it like this:
from excelgenerator import generate_bags_uk
...
uk_bag = generate_bags_uk.generate_bags(tshirts, form.cleaned_data['price_uk_bag'])
And I get TypeError: module is not callable. What am I doing wrong here ?
Try the following code instead -
from excelgenerator.generate_bags_uk import generate_bags
...
uk_bag = generate_bags(tshirts, form.cleaned_data['price_uk_bag'])
and make sure excelgenerator folder has a __init__.py inside it, otherwise it will not be dealt as a python package.
Also, I guess the method has a body, if it does not then at least give it a definition -
def generate_bags(bags, price):
pass
COMMENT : From the looks of this error, the error is happening in file /home/marijus/workspace/tshirtnation/excelgenerator/generate_bags_uk.py. I think your other calls are also of similar format and thus causing error. Please change them as I mentioned. The problem should be solved.
I am writing a test suite in python 2.6 using the unittest framework, and I want to use asserts in my code. I know that asserts got a complete overhaul and are much nicer in 2.7+ but I am confined to using 2.6 for now.
I am having problems using asserts. I want to be able to use the assertIn(a,b) feature, but alas, that is only in 2.7+. So I realized I must use the assertTrue(x) which is also in 2.6, but that didn't work. Then, I looked at this document which says that in previous versions assertTrue(x) used to be failUnless(x), so I used that in my code, and still no results.
I get the message:
NameError: global name 'failUnless' is not defined
which is the same thing I got for assertIn(a,b) and for assertTrue(x).
So I am totally at a loss for what I should do.
shorter version of my problem:
I want to be able to implement assertIn(a,b) in python 2.6.
Anyone have any solutions to this?
my code:
import unittest
class test_base(unittest.TestCase):
# some functions that are used by many tests
class test_01(test_base):
def setUp(self):
#set up code
def tearDown(self):
#tear down code
def test_01001_something(self):
#gets a return value of a function
ret = do_something()
#here i want to check if foo is in ret
failUnless("foo" in ret)
edit: Seems I am an idiot. All I needed to do was add self.assert.... and it worked.
import unittest
class MyTest(unittest.TestCase):
def test_example(self):
self.assertTrue(x)
This should work, based on the docs for unittest from Python 2.6. Be sure to use it as TestCase.assertTrue().
edit: In your example, set it as self.failUnless("foo" in ret) and it should work.
assertTrue should work just fine for an in test:
self.assertTrue('a' in somesequence)
All assertIn does is run the same test as above and set a helpful message if the test fails.
Your code for test case really helped.
Your problem is that you're trying to use assert[Something] as functions, while they're methods of TestCase class.
So you can solve your problem with, e.g. assertTrue:
self.assertTrue(element in list_object)
Actually implementing assertIn is pretty trivial. This is what I've used in my unit tests:
class MyTestCase(unittest.TestCase)
def assertIn(self, item, iterable):
self.assertTrue(item in iterable,
msg="{item} not found in {iterable}"
.format(item=item,
iterable=iterable))
You can then base all your testcases on this class instead unittest.TestCase and safely use assertIn even on python 2.6 and the error message will be much better than pure assertTrue. For comparison actual implementation of assertIn from Python 2.7:
def assertIn(self, member, container, msg=None):
"""Just like self.assertTrue(a in b), but with a nicer default message."""
if member not in container:
standardMsg = '%s not found in %s' % (safe_repr(member),
safe_repr(container))
self.fail(self._formatMessage(msg, standardMsg))