Django unittest result depends on test name - python

I predefined few dicts in my test.
eg.
TIME_DISCOUNT_OFFER = dict(
time_discount=dict(
discount_days=5,
discount_percent=15
)
)
This dict goes to my model (JSONfield), then in my test, I am taking data from it. When I name test test_add_the_rest_of_name django detect predefined dicts, but when I name test eg. test_time_sth django gives me output
discount = TIME_DISCOUNT_OFFER.get("time_discount").get("discount_percent")
AttributeError: 'NoneType' object has no attribute 'get'
I have noticed, that in the second case (test_time_sth) my predefined dict is empty according to django, no matter if I am taking data directly from dict or from model instance. If I name test differently, test pass. Does anyone have any clue why is it happening?
Minimal example:
Model:
from django.contrib.postgres.fields import JSONField
class Offer(models.Model):
details = JSONField()
Test:
TIME_DISCOUNT_OFFER = dict(
time_discount=dict(
discount_days=10,
discount_percent=25
)
)
class OfferTest(TestCase):
#classmethod
def setUpTestData(cls):
cls.offer = Offer.objects.create(details=TIME_DISCOUNT_OFFER)
def test_special_offer_removed(self):
self.offer.details.pop('time_discount', None)
def test_timeframe_offer(self):
discount = TIME_DISCOUNT_OFFER.get("time_discount").get("discount_percent")
def test_add_offer(self):
discount = TIME_DISCOUNT_OFFER.get("time_discount").get("discount_percent")
Traceback:
..E
======================================================================
ERROR: test_timeframe_offer (offers.tests.test_offer.OfferTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/admin1/Dokumenty/project/offers/tests/test_offer.py", line 142, in test_timeframe_offer
discount = TIME_DISCOUNT_OFFER.get("time_discount").get("discount_percent")
AttributeError: 'NoneType' object has no attribute 'get'
----------------------------------------------------------------------
Ran 3 tests in 0.006s
FAILED (errors=1)

You did not clean up properly after your tests. Everything that has been change in a test should be cleaned up at its end.
In your special case you are removing the key time_discount in your test test_special_offer_removed. Now depending on the order of the tests you might start your failing test after test_special_offer_removed and it will find the dictionary with the now missing key.
To prevent such problem make sure you are restoring the environment after every test: bring TIME_DISCOUNT_OFFER back to its original state at the end of test_special_offer_removed!

Related

Django - Full test suite failing when adding a TestCase, but full test suite passes when it is commented out. All TestCase pass when run individually

So this seems to be an issue talked about here and there on StackOverflow with no real solution. So I have a bunch of tests that all pass when run individual. They even pass when run as a full test suite, EXCEPT when I add in my TestCase ExploreFeedTest. Now ExploreFeedTest passes when run by itself and it actually doesn't fail when run in the full test suite as in running python manage.py test, it causes another test HomeTest to fail, which passes on it's own and passes when ExploreFeedTest is commented out from the init.py under the test folder. I hear this is an issue with Django not cleaning up data properly? All my TestCase classes are from django.test.TestCase, because apparently if you don't use that class Django doesn't teardown the data properly, so I don't really know how to solve this. I'm also running Django 3.2.9, which is supposedly the latest. Anyone have a solution for this?
ExploreFeedTest.py
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
class ExploreFeedTest(TestCase):
Folder setup
Here are some others having similar issue:
why would a django test fail only when the full test suite is run?
Inconsistent Django test results depending upon how the test is called in Django 1.5.1 running on Python 2.7.4
here's a small snippet of the failing test I am getting
.............................FE.E.E.EFE.E.E.EFEFE.E.E.E.E.E.E.E.E.E.E..............................
======================================================================
ERROR: test_all_posts_contains_post_by_user_followees_and_follow_goal (cheers.test.PostTests.HomeTest.HomeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\backends\utils.py", line 82, in _execute
return self.cursor.execute(sql)
psycopg2.errors.ForeignKeyViolation: insert or update on table "cheers_post" violates foreign key constraint "cheers_post_join_goal_id_da1e6957_fk_cheers_joingoal_uuid"
DETAIL: Key (join_goal_id)=(0947b806-f9f8-4e2c-98df-6072eaa61533) is not present in table "cheers_joingoal".
All the E I'm getting in the tests are essentially because of this. Keep in mind these all pass just fine when I comment out ExploreFeedTest and
ExploreFeedTest passes successfully when ran alone. Also as you can see it's HomeTest failing.
Here's an example of one of the tests that's failing due to this error as well
class HomeTest(TestCase):
#classmethod
# Generates Test DB data to persist throughout all tests
def setUpTestData(cls) -> None:
cls.generate_count = 4
cls.access_token = get_test_user_access_token()
cls.num_posts = 0
cls.user = create_test_user_in_DB()
GoalCategory.objects.create(name='health', emoji_url='url')
cls.goal = GoalFactory()
cls.join_goal = JoinGoal.objects.create(joiner=cls.user, goal=cls.goal)
# Posts by the current user
for i in range(cls.generate_count):
cls.num_posts += 1
PostFactory()
cls.followee = User.objects.create(uuid=uuid.uuid4(), username='Test')
followee_join_goal = JoinGoal.objects.create(joiner=cls.followee, goal=cls.goal)
FollowUser.objects.create(followee=cls.followee, follower=cls.user)
# Posts by user the current user is following
for i in range(cls.generate_count):
cls.num_posts += 1
Post.objects.create(creator=cls.followee, join_goal=followee_join_goal,
type=PostType.UPDATE, body='test')
random_user = User.objects.create(uuid=uuid.uuid4(), username='Random')
cls.followed_join_goal = JoinGoal.objects.create(joiner=random_user, goal=cls.goal)
FollowGoal.objects.create(follower=cls.user, join_goal=cls.followed_join_goal)
# Posts of goal current user is following
for i in range(cls.generate_count):
cls.num_posts += 1
Post.objects.create(creator=random_user, join_goal=cls.followed_join_goal,
type=PostType.UPDATE, body='test')
cls.count = int(cls.num_posts / 2) - 1
cls.post_list = list(Post.objects.all().order_by('uuid'))
cls.mid_idx, cls.mid_post = get_mid_idx_and_post_from_post_list(cls.post_list)
def test_all_posts_contains_post_by_user_followees_and_follow_goal(self):
response = self.client.get(reverse('get_initial_home_feed',
kwargs={'count': self.num_posts}),
**{'HTTP_AUTHORIZATION': f'bearer {self.access_token}'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), self.num_posts)
posts_by_user = list(Post.objects.filter(creator=self.user).values_list('uuid', flat=True))
posts_by_followees = list(Post.objects.filter(creator=self.followee).values_list('uuid', flat=True))
posts_of_followed_join_goal = list(
Post.objects.filter(join_goal=self.followed_join_goal).values_list('uuid', flat=True))
uuids_of_posts = [uuid.UUID(data['uuid']) for data in list(response.data)]
self.assertTrue(all(uuid in uuids_of_posts for uuid in posts_by_user))
self.assertTrue(all(uuid in uuids_of_posts for uuid in posts_by_followees))
self.assertTrue(all(uuid in uuids_of_posts for uuid in posts_of_followed_join_goal))
Solutions I've tried so far:
Change ordering of tests (ExploreFeedTest started failing and HomeTest all succeeded when I did this
I posted the answer on the stack overflow question
Django - Serializer throwing "Invalid pk - object does not exist" when setting ManyToMany attribute where foreign keyed object does exist
I was also using factory boy, which doesn't seem to play nice with test suite. Test suite doesn't seem to know how to rollback the DB without getting rid of factory boy generated data.

Error trying to use pyad to update user account

I'm trying to update a user attribute in Active Directory using pyad. This is my code
from pyad import *
pyad.set_defaults(ldap_server="server.domain.local",
username="admin", password="password")
pyad.adobject.ADObject.update_attribute(self='testuser1', attribute='mail',
newvalue='my#email.com')
and this is the error i recieve.
Traceback (most recent call last):
File "c:\Users\Administrator\Desktop\scripts\AD-Edit-user.py", line 12, in
<module>
pyad.adobject.ADObject.update_attribute(self='testuser1', attribute='mail',
newvalue='my#email.com')
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36-
32\lib\site-packages\pyad-0.5.20-py3.6.egg\pyad\adobject.py", line 318, in
update_attribute
elif pyadutils.generate_list(newvalue) != self.get_attribute(attribute):
AttributeError: 'str' object has no attribute 'get_attribute'
This makes me assume that I need to change the attribute type from str to something else. I have verified that mail is the correct attribute name.
I know ldap connection is working because i can create a user using a similar script.
Your problem is how you use pyad.
Specifically in this line:
pyad.adobject.ADObject.update_attribute(self='testuser1', attribute='mail',
newvalue='my#email.com')
if we look at the source of pyad.adobject.ADObject, we can see the following:
def update_attribute(self, attribute, newvalue, no_flush=False):
"""Updates any mutable LDAP attribute for the object. If you are adding or removing
values from a multi-valued attribute, see append_to_attribute and remove_from_attribute."""
if newvalue in ((),[],None,''):
return self.clear_attribute(attribute)
elif pyadutils.generate_list(newvalue) != self.get_attribute(attribute):
self._set_attribute(attribute, 2, pyadutils.generate_list(newvalue))
if not no_flush:
self._flush()
self here is not a parameter of the function, it is a reference to the class instance. Your call includes self='testuser1' which is str.
Please lookup the documentation on how to use this function / functionality / module, here. You will notice that there is no "self"... other than looking into the source code I am not sure how you got to the conclusion you needed self.
I have no way of testing the following, but this is roughly how it should work:
# first you create an instance of the object, based on
# the distinguished name of the user object which you
# want to change
my_ad_object = pyad.adobject.ADObject.from_dn('the_distinguished_name')
# then you call the update_attribute() method on the instance
my_ad_object.update_attribute(attribute='mail', newvalue='my#email.com')

AttributeError when using request.function in pytest yield fixture

I have several pytest test cases that need nearly identical setup, so I would like to have them reuse a fixture to keep things DRY. The setup involves creating a new ticket in an external ticket tracking system, then the test cases interact with the ticket based on the data, and finally the fixture cleans up by closing out the ticket. The challenge here is that each test case needs slightly different data to be prepped in the ticket.
Each test case has different calls and different assertions, so I can't combine them all into a single parametrized test case with a single test fixture. Parametrizing the the fixture itself would result in every test case running every permutation of the fixture data, which ends up with a lot of irrelevant test failures.
What I would like to do is set a variable in the test case, then have the fixture use that variable to set up the test data when creating the ticket. I've tried to use request.function as specified in the pytest fixture docs but I keep getting:
=================================== ERRORS ===================================
____________________ ERROR at setup of TestMCVE.test_stuff ___________________
request = <SubRequest 'ticket' for <Function 'test_stuff'>>
#pytest.yield_fixture
def ticket(request):
> ticket_summary = getattr(request.function, "summary")
E AttributeError: 'function' object has no attribute 'summary'
tests\test_mcve.py:11: AttributeError
My code is:
import pytest
def ticket_system_api(summary):
# stub for MCVE purposes
return summary
#pytest.yield_fixture
def ticket(request):
ticket_summary = getattr(request.function, "summary")
new_ticket = ticket_system_api(summary=ticket_summary)
yield new_ticket
class TestMCVE:
def test_stuff(self, ticket):
summary = 'xyz'
# do real things here, except MCVE
assert 'xyz' == ticket
I've tried using request.node instead of request.function as well as binding the summary variable per this answer, changing summary = 'xyz' to test_stuff.summary = 'xyz' but these still fail with the same AttributeError.
How can I pass the function level data to the fixture?
You can accomplish this with indirect parametrization. The API (and the documentation) could be friendlier, but the functionality you want is there.
Your example was very close, and minor tweaks were needed. Take a look:
import pytest
def ticket_system_api(summary):
# stub for MCVE purposes
return summary
#pytest.fixture
def ticket(request):
# NOTE: This will raise `AttributeError` if the fixture
# doesn't receive a parameter.
ticket_summary = request.param
new_ticket = ticket_system_api(summary=ticket_summary)
return new_ticket
class TestMCVE:
#pytest.mark.parametrize('ticket', ('abc',), indirect=True)
def test_abc(self, ticket):
# do real things here, except MCVE
assert ticket == 'abc'
#pytest.mark.parametrize('ticket', ('xyz',), indirect=True)
def test_xyz(self, ticket):
# do real things here, except MCVE
assert ticket == 'xyz'

odoo 9 AttributeError: 'NoneType' object has no attribute 'find'

I am getting attribute error while evaluating the code, the code is working fine for odoo 8 but in odoo 9 it gives error.
Please suggest me the solution.
Here below is my code
def approve_loan(self ,cr ,uid ,ids ,context=None):
move_pool = self.pool.get('account.move')
period_pool = self.pool.get('account.period')
timenow = time.strftime('%Y-%m-%d')
loan_slip = self.browse(cr ,uid ,ids)[0]
line_ids = []
# get period
ctx = dict(context or {}, account_period_prefer_normal=True)
search_periods = period_pool.find(cr, uid, loan_slip.start_date, context=ctx)
period_id = search_periods[0]
Why you get the error
None is returned at the moment the model doesn't exist.
Possible Reasons it returns none
You probably didn't the module needed for the model "account.period". (You should consider adding it to the dependencies of your module.)
Odoo changed their modules so the model "account.period" doesn't exist anymore (You should figure out what it's replaced by and edit your code to use the new Odoo9 code)

django populating queryset into a dictionary

am not quite advanced python user, I am stuck trying to populate the below but I think am handling list_choices wrong.
class LocationManager(TranslationManager):
def get_location_list(self, lang_code, site=None):
# this function is for building a list to be used in the posting process
# TODO: tune the query to hit database only once
list_choices = {}
for parents in self.language(lang_code).filter(country__site=site, parent=None):
list_child = ((child.id, child.name) for child in self.language(lang_code).filter(parent=parents))
list_choices.setdefault(parents).append(list_child)
return list_choices
Below the error am getting
>>>
>>> Location.objects.get_location_list(lang_code='en', site=current_site)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/mo/Projects/mazban/mazban/apps/listing/geo/models.py", line 108, in get_location_list
list_choices.setdefault(parents).append(list_child)
AttributeError: 'NoneType' object has no attribute 'append'
That is because you use setdefault without a second argument. And it returns None in this case.
Try this fixed code:
# this is much more confinient and clearer
from collections import defaultdict
def get_location_list(self, lang_code, site=None):
# this function is for building a list to be used in the posting process
# TODO: tune the query to hit database only once
list_choices = defaultdict(list)
for parent in self.language(lang_code).filter(country__site=site, parent=None):
list_child = self.language(lang_code).filter(parent=parent).values_list('id', 'name')
list_choices[parent].extend(list_child)
return list_choices

Categories

Resources