Skip to content

Chapter 15: Testing ​

15.2 Integration Testing ​

LiveServerTestCase ​

LiveServerTestCase is used to run integration tests that require a real HTTP server:

python
# test_integration.py
from django.test import LiveServerTestCase
from django.contrib.auth.models import User
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
import time

class BlogIntegrationTest(LiveServerTestCase):
    """Blog integration test"""
    
    @classmethod
    def setUpClass(cls):
        """Test class initialization"""
        super().setUpClass()
        
        # Configure Chrome options
        chrome_options = Options()
        chrome_options.add_argument('--headless')  # Headless mode
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--disable-dev-shm-usage')
        
        # Start Chrome browser
        cls.browser = webdriver.Chrome(options=chrome_options)
        cls.browser.implicitly_wait(10)
    
    @classmethod
    def tearDownClass(cls):
        """Test class cleanup"""
        cls.browser.quit()
        super().tearDownClass()
    
    def setUp(self):
        """Setup before each test"""
        # Create test user
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.user.save()
        
        # Create test article
        from .models import Article, Category
        self.category = Category.objects.create(name='Test Category')
        self.article = Article.objects.create(
            title='Test Article',
            author=self.user,
            category=self.category,
            content='This is the content of the test article',
            status='published'
        )
        
        super().setUp()
    
    def test_user_can_view_article_list(self):
        """Test that users can view the article list"""
        # Visit article list page
        self.browser.get(f'{self.live_server_url}/')
        
        # Wait for page to load
        WebDriverWait(self.browser, 10).until(
            EC.presence_of_element_located((By.TAG_NAME, "h1"))
        )
        
        # Check page title
        self.assertIn('Blog', self.browser.title)
        
        # Check if article title is displayed
        page_text = self.browser.find_element(By.TAG_NAME, "body").text
        self.assertIn('Test Article', page_text)
    
    def test_user_can_view_article_detail(self):
        """Test that users can view article details"""
        # Visit article detail page
        self.browser.get(
            f'{self.live_server_url}/articles/{self.article.slug}/'
        )
        
        # Wait for page to load
        WebDriverWait(self.browser, 10).until(
            EC.presence_of_element_located((By.TAG_NAME, "h1"))
        )
        
        # Check article title
        title_element = self.browser.find_element(By.TAG_NAME, "h1")
        self.assertEqual(title_element.text, 'Test Article')
        
        # Check article content
        content_element = self.browser.find_element(By.CLASS_NAME, "article-content")
        self.assertIn('This is the content of the test article', content_element.text)
    
    def test_user_can_login_and_create_article(self):
        """Test that users can login and create articles"""
        # Visit login page
        self.browser.get(f'{self.live_server_url}/accounts/login/')
        
        # Fill login form
        username_input = self.browser.find_element(By.NAME, "username")
        password_input = self.browser.find_element(By.NAME, "password")
        login_button = self.browser.find_element(By.XPATH, "//button[@type='submit']")
        
        username_input.send_keys('testuser')
        password_input.send_keys('testpass123')
        login_button.click()
        
        # Wait for successful login and redirect
        WebDriverWait(self.browser, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "alert-success"))
        )
        
        # Visit create article page
        self.browser.get(f'{self.live_server_url}/articles/new/')
        
        # Fill article form
        title_input = self.browser.find_element(By.NAME, "title")
        content_input = self.browser.find_element(By.NAME, "content")
        submit_button = self.browser.find_element(By.XPATH, "//button[@type='submit']")
        
        title_input.send_keys('New Test Article')
        content_input.send_keys('This is the content of the new test article')
        submit_button.click()
        
        # Wait for article creation success
        WebDriverWait(self.browser, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "alert-success"))
        )
        
        # Verify article was created
        self.assertIn('Article created successfully', self.browser.page_source)

# Using Playwright for more modern browser testing
# test_playwright.py
import pytest
from django.test import LiveServerTestCase
from playwright.sync_api import sync_playwright

class PlaywrightBlogTest(LiveServerTestCase):
    """Blog test using Playwright"""
    
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.playwright = sync_playwright().start()
        cls.browser = cls.playwright.chromium.launch(headless=True)
        cls.page = cls.browser.new_page()
    
    @classmethod
    def tearDownClass(cls):
        cls.browser.close()
        cls.playwright.stop()
        super().tearDownClass()
    
    def test_article_search_functionality(self):
        """Test article search functionality"""
        # Create test data
        from .models import Article, Category
        from django.contrib.auth.models import User
        
        user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        category = Category.objects.create(name='Test Category')
        Article.objects.create(
            title='Python Programming Guide',
            author=user,
            category=category,
            content='Python is a powerful programming language',
            status='published'
        )
        Article.objects.create(
            title='JavaScript Introduction',
            author=user,
            category=category,
            content='JavaScript is the core language of frontend development',
            status='published'
        )
        
        # Visit homepage
        self.page.goto(f"{self.live_server_url}/")
        
        # Use search function
        search_input = self.page.locator("input[name='q']")
        search_input.fill("Python")
        search_input.press("Enter")
        
        # Verify search results
        self.page.wait_for_selector(".article-list")
        search_results = self.page.locator(".article-item")
        assert search_results.count() == 1
        assert "Python Programming Guide" in self.page.content()

Selenium Integration ​

More examples of Selenium integration testing:

python
# test_selenium_advanced.py
from django.test import LiveServerTestCase
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options

class AdvancedSeleniumTest(LiveServerTestCase):
    """Advanced Selenium test"""
    
    def setUp(self):
        """Test setup"""
        chrome_options = Options()
        chrome_options.add_argument('--headless')
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--disable-dev-shm-usage')
        
        self.browser = webdriver.Chrome(options=chrome_options)
        self.browser.implicitly_wait(10)
        
        # Create test data
        from django.contrib.auth.models import User
        from .models import Article, Category, Comment
        
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        self.category = Category.objects.create(name='Test Category')
        self.article = Article.objects.create(
            title='Test Article',
            author=self.user,
            category=self.category,
            content='Test article content',
            status='published'
        )
        
        super().setUp()
    
    def tearDown(self):
        """Test cleanup"""
        self.browser.quit()
        super().tearDown()
    
    def test_comment_system(self):
        """Test comment system"""
        # Login
        self.browser.get(f'{self.live_server_url}/accounts/login/')
        self.browser.find_element(By.NAME, "username").send_keys('testuser')
        self.browser.find_element(By.NAME, "password").send_keys('testpass123')
        self.browser.find_element(By.XPATH, "//button[@type='submit']").click()
        
        # Visit article detail
        self.browser.get(
            f'{self.live_server_url}/articles/{self.article.slug}/'
        )
        
        # Add comment
        comment_textarea = self.browser.find_element(By.NAME, "content")
        comment_textarea.send_keys('This is a test comment')
        
        submit_button = self.browser.find_element(
            By.XPATH, "//button[contains(text(), 'Post Comment')]"
        )
        submit_button.click()
        
        # Verify comment was added
        WebDriverWait(self.browser, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "comment-content"))
        )
        
        comment_content = self.browser.find_element(
            By.CLASS_NAME, "comment-content"
        ).text
        self.assertIn('This is a test comment', comment_content)
    
    def test_responsive_design(self):
        """Test responsive design"""
        # Test desktop version
        self.browser.set_window_size(1920, 1080)
        self.browser.get(f'{self.live_server_url}/')
        
        # Check desktop navigation
        desktop_nav = self.browser.find_element(By.CLASS_NAME, "navbar-desktop")
        self.assertTrue(desktop_nav.is_displayed())
        
        # Test mobile version
        self.browser.set_window_size(375, 667)
        self.browser.refresh()
        
        # Check mobile navigation
        mobile_nav_toggle = self.browser.find_element(By.CLASS_NAME, "navbar-toggle")
        self.assertTrue(mobile_nav_toggle.is_displayed())
    
    def test_form_validation(self):
        """Test form validation"""
        # Login
        self.browser.get(f'{self.live_server_url}/accounts/login/')
        self.browser.find_element(By.NAME, "username").send_keys('testuser')
        self.browser.find_element(By.NAME, "password").send_keys('testpass123')
        self.browser.find_element(By.XPATH, "//button[@type='submit']").click()
        
        # Visit create article page
        self.browser.get(f'{self.live_server_url}/articles/new/')
        
        # Submit empty form
        submit_button = self.browser.find_element(By.XPATH, "//button[@type='submit']")
        submit_button.click()
        
        # Verify error messages
        error_messages = self.browser.find_elements(By.CLASS_NAME, "error-message")
        self.assertGreater(len(error_messages), 0)
        
        # Fill in valid data and submit
        self.browser.find_element(By.NAME, "title").send_keys('Valid Title')
        self.browser.find_element(By.NAME, "content").send_keys('Valid Content')
        submit_button.click()
        
        # Verify success message
        success_message = self.browser.find_element(By.CLASS_NAME, "alert-success")
        self.assertTrue(success_message.is_displayed())

Test Coverage ​

Configuring and using test coverage tools:

python
# Install coverage tool
# pip install coverage

# .coveragerc configuration file
"""
[run]
source = .
omit = 
    */venv/*
    */env/*
    */migrations/*
    */tests/*
    manage.py
    */settings/*
    */wsgi.py
    */asgi.py

[report]
exclude_lines =
    pragma: no cover
    def __repr__
    raise AssertionError
    raise NotImplementedError
    if __name__ == .__main__.:

[html]
directory = coverage_html_report
"""

# Run tests with coverage
# coverage run --source='.' manage.py test
# coverage report
# coverage html

# pytest configuration file pytest.ini
"""
[pytest]
DJANGO_SETTINGS_MODULE = myproject.settings
python_files = tests.py test_*.py *_tests.py
addopts = --cov=. --cov-report=html --cov-report=term
"""

# Using pytest for testing
# test_pytest.py
import pytest
from django.test import Client
from django.contrib.auth.models import User
from .models import Article, Category

@pytest.fixture
def client():
    """Test client fixture"""
    return Client()

@pytest.fixture
def user():
    """User fixture"""
    return User.objects.create_user(
        username='testuser',
        password='testpass123'
    )

@pytest.fixture
def article(user):
    """Article fixture"""
    category = Category.objects.create(name='Test Category')
    return Article.objects.create(
        title='Test Article',
        author=user,
        category=category,
        content='Test content',
        status='published'
    )

def test_article_list_view(client, article):
    """Test article list view"""
    response = client.get('/')
    assert response.status_code == 200
    assert 'Test Article' in str(response.content)

def test_article_detail_view(client, article):
    """Test article detail view"""
    response = client.get(f'/articles/{article.slug}/')
    assert response.status_code == 200
    assert 'Test Article' in str(response.content)

@pytest.mark.django_db
def test_article_creation():
    """Test article creation"""
    user = User.objects.create_user(
        username='testuser',
        password='testpass123'
    )
    category = Category.objects.create(name='Test Category')
    
    article = Article.objects.create(
        title='New Article',
        author=user,
        category=category,
        content='New article content',
        status='published'
    )
    
    assert article.title == 'New Article'
    assert article.is_published

# Performance test example
import time
from django.test import TestCase

class PerformanceTest(TestCase):
    """Performance test"""
    
    def setUp(self):
        # Create large amount of test data
        from django.contrib.auth.models import User
        from .models import Article, Category
        
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        self.category = Category.objects.create(name='Test Category')
        
        # Create 1000 articles for performance testing
        for i in range(1000):
            Article.objects.create(
                title=f'Test Article{i}',
                author=self.user,
                category=self.category,
                content=f'Test content{i}',
                status='published'
            )
    
    def test_article_list_performance(self):
        """Test article list performance"""
        start_time = time.time()
        
        # Execute 100 queries
        for _ in range(100):
            response = self.client.get('/articles/')
            self.assertEqual(response.status_code, 200)
        
        end_time = time.time()
        total_time = end_time - start_time
        
        # Assert average response time is less than 100ms
        average_time = total_time / 100
        self.assertLess(average_time, 0.1)

Continuous Integration ​

Configuring continuous integration environment:

yaml
# .github/workflows/test.yml
name: Django CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
      
      redis:
        image: redis:6
        ports:
          - 6379:6379
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: 3.9
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install coverage
    
    - name: Run migrations
      run: python manage.py migrate
    
    - name: Run tests with coverage
      run: coverage run --source='.' manage.py test
    
    - name: Generate coverage report
      run: |
        coverage report
        coverage xml
    
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v1
      with:
        file: ./coverage.xml
        flags: unittests
        name: codecov-umbrella

Through these integration testing practices, you can ensure that the application's functionality and performance in a real environment meet expectations.

Summary ​

Core points of Django integration testing:

  1. ✅ Use LiveServerTestCase for browser integration testing
  2. ✅ Master the use of tools like Selenium and Playwright
  3. ✅ Implement test coverage analysis to ensure code quality
  4. ✅ Configure continuous integration environment to automate testing processes
  5. ✅ Focus on performance testing and response time optimization

Integration testing can verify that collaboration between system components is working properly.

Next Article ​

We will learn about database optimization techniques.

16.1 Database Optimization →

Directory ​

Return to Course Directory

Released under the [BY-NC-ND License](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.en).