テストとCI/CD
Django TestCase
# blog/tests.py
from django.test import TestCase, Client
from django.contrib.auth import get_user_model
from .models import Post, Category
User = get_user_model()
class PostModelTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user(
username='testuser', password='testpass123'
)
cls.category = Category.objects.create(
name='Python', slug='python'
)
cls.post = Post.objects.create(
title='テスト記事',
slug='test-post',
author=cls.user,
body='本文です。',
status='published'
)
def test_post_str(self):
self.assertEqual(str(self.post), 'テスト記事')
def test_post_ordering(self):
posts = Post.objects.all()
self.assertEqual(posts.first(), self.post)
class PostViewTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='testuser', password='testpass123'
)
def test_post_list_view(self):
response = self.client.get('/blog/')
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'blog/post_list.html')
def test_post_create_requires_login(self):
response = self.client.get('/blog/create/')
self.assertEqual(response.status_code, 302) # リダイレクト
def test_post_create_authenticated(self):
self.client.login(username='testuser', password='testpass123')
response = self.client.post('/blog/create/', {
'title': '新しい記事',
'body': '内容です。',
'status': 'draft',
})
self.assertEqual(response.status_code, 302)
self.assertTrue(Post.objects.filter(title='新しい記事').exists())
APIテスト(DRF)
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
class PostAPITest(APITestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser', password='testpass123'
)
self.client = APIClient()
def test_list_posts(self):
response = self.client.get('/api/posts/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_create_post_unauthenticated(self):
response = self.client.post('/api/posts/', {'title': 'Test'})
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_create_post_authenticated(self):
self.client.force_authenticate(user=self.user)
response = self.client.post('/api/posts/', {
'title': 'APIから作成',
'body': '本文',
'status': 'draft',
})
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
factory_boy でテストデータ作成
import factory
from django.contrib.auth import get_user_model
from blog.models import Post
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = get_user_model()
username = factory.Sequence(lambda n: f'user{n}')
email = factory.LazyAttribute(lambda o: f'{o.username}@example.com')
class PostFactory(factory.django.DjangoModelFactory):
class Meta:
model = Post
title = factory.Sequence(lambda n: f'記事{n}')
slug = factory.Sequence(lambda n: f'post-{n}')
author = factory.SubFactory(UserFactory)
body = factory.Faker('text', locale='ja_JP')
status = 'published'
GitHub Actions
# .github/workflows/test.yml
name: Django Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
ports: ['5432:5432']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install -r requirements.txt
- run: python manage.py test
env:
DATABASE_URL: postgres://testuser:testpass@localhost/testdb
まとめ
TestCase でモデル・ビューのテストを記述
APITestCase でREST APIのテストを記述
factory_boy でテストデータを効率的に作成
- GitHub Actions でプッシュ/PR時に自動テスト実行
- テストは
python manage.py test で実行