記事一覧へ戻る 本の順番で続きを読む

テストとCI/CD - 品質保証の自動化

Python3上級 | 2026/02/18 21:20

テストと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 で実行