Selenium / Django给出外键错误

时间:2013-08-16 19:53:59

标签: django firefox selenium functional-testing selenium-chromedriver

我正在尝试使用Selenium为Django项目运行功能测试。在Firefox和Chrome上,当我尝试测试超级用户可以将普通用户的状态更改为工作人员时,我得到一个奇怪的外键错误(我假设这都是通过Django的内部测试验证的,但是认为包括这样做是好的做法它在我的应用程序测试中,因为我的用户场景取决于功能)。看起来Django几乎不喜欢Selenium将任何内容保存到数据库中?这个错误也会逐渐渗透到我的其他测试中,所以看起来像是在幕后打破了一些东西 - 比如Selenium失去了它的数据库连接。有谁知道为什么会这样?我愿意放弃测试以支持假设功能正常,但是我很乐意解决这个问题并使用测试。

一个可能的症状是,在Chrome中,在“选中框”和“单击保存按钮”的输出之间,我得到了一个损坏的管道(在Firefox中不会发生)。

每当Selenium点击“保存”按钮时,它似乎在这里徘徊:

# Form looks properly rendered, now click the 'Staff status'
# checkbox and submit it
isStaffCheckbox = self.browser.find_element_by_id('id_is_staff')
isStaffCheckbox.click()               
print 'checked the box'
# Save the form
saveBtn = self.browser.find_element_by_css_selector('input[value="Save"]')
saveBtn.click()
print 'clicked the save button'

我得到的错误信息是:

Traceback (most recent call last):
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/test/testcases.py", line 268, in __call__
    self._post_teardown()
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/test/testcases.py", line 533, in _post_teardown
    self._fixture_teardown()
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/test/testcases.py", line 553, in _fixture_teardown
    skip_validation=True, reset_sequences=False)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/__init__.py", line 161, in call_command
    return klass.execute(*args, **defaults)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/base.py", line 255, in execute
    output = self.handle(*args, **options)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/base.py", line 385, in handle
    return self.handle_noargs(**options)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/commands/flush.py", line 82, in handle_noargs
    emit_post_sync_signal(set(all_models), verbosity, interactive, db)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/core/management/sql.py", line 195, in emit_post_sync_signal
    interactive=interactive, db=db)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/dispatch/dispatcher.py", line 170, in send
    response = receiver(signal=self, sender=sender, **named)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/contrib/auth/management/__init__.py", line 96, in create_permissions
    auth_app.Permission.objects.using(db).bulk_create(perms)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/query.py", line 444, in bulk_create
    self._batched_insert(objs_without_pk, fields, batch_size)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/query.py", line 902, in _batched_insert
    using=self.db)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/manager.py", line 215, in _insert
    return insert_query(self.model, objs, fields, **kwargs)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/query.py", line 1661, in insert_query
    return query.get_compiler(using=using).execute_sql(return_id)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 937, in execute_sql
    cursor.execute(sql, params)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/backends/mysql/base.py", line 122, in execute
    six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/django/db/backends/mysql/base.py", line 120, in execute
    return self.cursor.execute(query, args)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/MySQL_python-1.2.4b4-py2.7-macosx-10.8-intel.egg/MySQLdb/cursors.py", line 202, in execute
    self.errorhandler(self, exc, value)
File "~/virtual_environments/VideoSearch/lib/python2.7/site-packages/MySQL_python-1.2.4b4-py2.7-macosx-10.8-intel.egg/MySQLdb/connections.py", line 36, in defaulterrorhandler
    raise errorclass, errorvalue
IntegrityError: (1452, 'Cannot add or update a child row: a foreign key constraint fails (`test_videos2002`.`auth_permission`, CONSTRAINT `content_type_id_refs_id_d043b34a` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`))')

===== 更新了代码

  • 另请注意,test_admin_can_make_a_user_staff导致我的其他测试错误输出同样的错误 - 但是当我从该测试中取出“save”命令时它们没问题。
  • 我想我真正不理解的两件事是:1)为什么这会发生在内置的Django管理视图中(认为它应该正常工作),以及2)为什么一个测试错误会流入我的其他测试?我以为他们是独立的。

感谢您的关注!

来自我的functional_tests.test.py(借鉴https://github.com/lincolnloop/django-selenium-intro/tree/master/selenium_intro):

from django.test import LiveServerTestCase

class SeleniumTestCase(LiveServerTestCase):
    """
    A base test case for selenium, providing hepler methods for generating
    clients and logging in profiles.
    """

    def open(self, url):
        self.browser.get("%s%s" % (self.live_server_url, url))

来自我的admin_django.py(一组测试用例)

from functional_tests.test import SeleniumTestCase

from selenium.webdriver.common.keys import Keys
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.test import LiveServerTestCase
from django.conf import settings
from selenium import webdriver


class AdminDjango(SeleniumTestCase):
    def setUp(self):
        User.objects.create_superuser(username='vcb',
                                      password='rock5!',
                                      email='me@name.edu')
        User.objects.create_user(username='teaching',
                                 password='assistant',
                                 email='ta@name.edu')
#        self.browser = webdriver.Chrome(settings.SELENIUM_WEBDRIVER)
        self.browser = webdriver.Firefox()
        self.browser.implicitly_wait(3)
        self.browser.set_page_load_timeout(10)

    def tearDown(self):
        self.browser.quit()

    def check_for_links(self, link_text):
        """
        Helper function to check links on a page for certain text
        """
        links = self.browser.find_elements_by_tag_name('a')
        self.assertTrue(link_text, [link.text for link in links])

    def admin_logs_in(self):
        """
        Helper function that logs the admin user into the page
        """
        username_field = self.browser.find_element_by_name('username')
        username_field.send_keys('vcb')

        password_field = self.browser.find_element_by_name('password')
        password_field.send_keys('rock5!')
        password_field.send_keys(Keys.RETURN)

    def admin_log_in_complete(self):
        """
        Includes navigation to the admin page
        """
        self.open('/admin/')
        self.admin_logs_in()

    def test_admin_can_login(self):
        """
        Admin user can log into the Django admin interface
        """
        self.open('/admin/')
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('VCB Administration', body.text)

        self.admin_logs_in()

        # her username and password are accepted, and she is taken to
        # the Site Administration page
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Site administration', body.text)

    def test_admin_page_renders_properly(self):
        """
        The admin page should have at least two fields:
         - Users
         - Classes
        Admins may have to add staff status to users, and they may have to
        adjust the information for a class
        """
        self.admin_log_in_complete()

        self.check_for_links('Users')
        self.check_for_links('Groups')
        self.check_for_links('Classess')

    def test_admin_can_make_a_user_staff(self):
        """
        Admin users can add staff status to existing users
        """
        self.admin_log_in_complete()
        pageLinks = self.browser.find_elements_by_tag_name('a')

        for link in pageLinks:
            if link.text == 'Users':
                userLink = link

        userLink.click()

        headers = self.browser.find_elements_by_tag_name('h1')
        self.assertTrue('Select user to change', 
                [header.text for header in headers])

        users =     self.browser.find_elements_by_xpath('//table[@id="result_list"]/tbody/tr/th/a')

        self.fail('Finish writing the test!')
#        rowCount = 1
#        for user in users:
#            xpath = '//table[@id="result_list"]/tbody/tr[' + str(rowCount) + ']/td[5]/img'
#            # check first that this user is not a staff
#            staffIcon = self.browser.find_element_by_xpath(xpath)
#            isStaff = staffIcon.get_attribute('alt')
#            
#            if isStaff == 'false':
#                user.click()
#                userHeaders = self.browser.find_elements_by_tag_name('h1')
#                self.assertTrue('Change user', 
#                        [userHeader.text for userHeader in userHeaders])
#            
#                # Are the right fields present in the user's form?
#                formHeaders = self.browser.find_elements_by_tag_name('h2')
#                self.assertTrue('Personal info', 
#                        [formHeader.text for formHeader in formHeaders])
#                self.assertTrue('Permissions', 
#                        [formHeader.text for formHeader in formHeaders])
#                self.assertTrue('Important dates', 
#                        [formHeader.text for formHeader in formHeaders])
#            
#                # Form looks properly rendered, now click the 'Staff status'
#                # checkbox and submit it
#                isStaffCheckbox = self.browser.find_element_by_id('id_is_staff')
#                isStaffCheckbox.click()
#                print 'checked the box'
#                # Save the form
#                saveBtn = self.browser.find_element_by_css_selector('input[value="Save"]')
#                saveBtn.click()
#                print 'clicked the save button'
#                # Returns you to the admin page
#                messageBox = self.browser.find_element_by_class_name('info')
#                self.assertIn('successfully', messageBox.text)
#                
#                # Check that staff status changed
#                staffIcon = self.browser.find_element_by_xpath('//table[@id="result_list"]/tbody/tr[' + str(rowCount) + ']/td[5]/img')
#                isStaff = staffIcon.get_attribute('alt')
#                self.assertTrue(isStaff)
#                print 'should now be staff'
#            rowCount += 1


    def test_admin_can_change_a_class_obj_bank_id(self):
        """
        Admin users can change a class's objective bank id
        """
        self.fail('Finish writing the test!')

    def test_logging_out_redirects_to_login_page(self):
        """
        Logging out of the admin page should redirect to the main page
        """
        self.admin_log_in_complete()
        logOut = self.browser.find_element_by_link_text('Log out')
        logOut.click()
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('VCB Administration', body.text)

1 个答案:

答案 0 :(得分:20)

正如我在评论中提到的,这似乎是Django中记录的错误。 Herehere是官方错误报告。如第二个链接中所述,一种解决方法是在django.contrib.auth中交换django.contrib.contenttypesINSTALLED_APPS的顺序,如下所示:

原来是什么:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'testapp'
)

应该成为:

INSTALLED_APPS = (
    'django.contrib.contenttypes',
    'django.contrib.auth',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'testapp'
)