4

我正在尝试使用 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 会导致我的其他测试出错,并出现相同的错误——但是当我从该测试中取出“保存”命令时它们是可以的。
  • 我想有两件事我真的不明白: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)
4

1 回答 1

21

正如我在评论中提到的,这似乎是 Django 中记录的错误。这里这里是官方的错误报告。如第二个链接中所述,一种解决方法是交换django.contrib.authdjango.contrib.contenttypesin的顺序INSTALLED_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'
)
于 2013-08-17T18:34:10.813 回答