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-umbrellaThrough 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:
- ✅ Use LiveServerTestCase for browser integration testing
- ✅ Master the use of tools like Selenium and Playwright
- ✅ Implement test coverage analysis to ensure code quality
- ✅ Configure continuous integration environment to automate testing processes
- ✅ 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.