kagomen / libraku

📚 リブラク | 図書館でのリクエストカード記入をラクにする書籍検索アプリ
https://libraku.pages.dev
0 stars 0 forks source link

テストする #121

Open kagomen opened 1 month ago

kagomen commented 1 month ago

参考

kagomen commented 3 weeks ago
kagomen commented 3 weeks ago

書籍検索アプリケーションの場合、以下のようなテストを実装することをお勧めします。各テストタイプごとに具体的な例を挙げ、ファイル構成についても説明します。

  1. ユニットテスト (Vitest)

テスト対象:

例:

// src/utils/search.test.ts
import { describe, it, expect } from 'vitest'
import { searchBooks } from './search'

describe('searchBooks', () => {
  it('should return matching books', async () => {
    const result = await searchBooks('JavaScript')
    expect(result.length).toBeGreaterThan(0)
    expect(result[0]).toHaveProperty('title')
  })

  it('should return empty array for no matches', async () => {
    const result = await searchBooks('xyzxyzxyz')
    expect(result).toEqual([])
  })
})

// src/utils/favorites.test.ts
import { describe, it, expect } from 'vitest'
import { addToFavorites, removeFromFavorites } from './favorites'

describe('Favorites', () => {
  it('should add a book to favorites', () => {
    const userId = 'user1'
    const bookId = 'book1'
    const result = addToFavorites(userId, bookId)
    expect(result).toBeTruthy()
  })

  it('should remove a book from favorites', () => {
    const userId = 'user1'
    const bookId = 'book1'
    const result = removeFromFavorites(userId, bookId)
    expect(result).toBeTruthy()
  })
})
  1. コンポーネントテスト (Vitest + React Testing Library)

テスト対象:

例:

// src/components/SearchForm.test.tsx
import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import SearchForm from './SearchForm'

describe('SearchForm', () => {
  it('should call onSearch when form is submitted', () => {
    const mockOnSearch = vi.fn()
    render(<SearchForm onSearch={mockOnSearch} />)

    const input = screen.getByPlaceholderText('Search books...')
    fireEvent.change(input, { target: { value: 'React' } })

    const submitButton = screen.getByText('Search')
    fireEvent.click(submitButton)

    expect(mockOnSearch).toHaveBeenCalledWith('React')
  })
})

// src/components/BookList.test.tsx
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import BookList from './BookList'

describe('BookList', () => {
  it('should render books correctly', () => {
    const books = [
      { id: '1', title: 'React Basics', author: 'John Doe' },
      { id: '2', title: 'Advanced JavaScript', author: 'Jane Smith' },
    ]
    render(<BookList books={books} />)

    expect(screen.getByText('React Basics')).toBeInTheDocument()
    expect(screen.getByText('Advanced JavaScript')).toBeInTheDocument()
  })
})
  1. 統合テスト (Vitest)

テスト対象:

例:

// src/integration/searchFlow.test.ts
import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import App from '../App'

describe('Search Flow', () => {
  it('should search and display results', async () => {
    render(<App />)

    const searchInput = screen.getByPlaceholderText('Search books...')
    fireEvent.change(searchInput, { target: { value: 'JavaScript' } })

    const searchButton = screen.getByText('Search')
    fireEvent.click(searchButton)

    await waitFor(() => {
      expect(screen.getByText('Search Results')).toBeInTheDocument()
      expect(screen.getAllByTestId('book-item').length).toBeGreaterThan(0)
    })
  })
})
  1. E2Eテスト (Cypress)

テスト対象:

例:

// cypress/integration/bookSearch.spec.js
describe('Book Search and Favorite', () => {
  it('should allow a user to register, search for a book, and add it to favorites', () => {
    cy.visit('/')

    // ユーザー登録
    cy.get('[data-testid=register-link]').click()
    cy.get('[data-testid=username-input]').type('testuser')
    cy.get('[data-testid=password-input]').type('password123')
    cy.get('[data-testid=register-button]').click()

    // 検索
    cy.get('[data-testid=search-input]').type('JavaScript')
    cy.get('[data-testid=search-button]').click()

    // 結果の確認
    cy.get('[data-testid=book-item]').should('have.length.greaterThan', 0)

    // お気に入り登録
    cy.get('[data-testid=favorite-button]').first().click()

    // お気に入りページでの確認
    cy.get('[data-testid=favorites-link]').click()
    cy.get('[data-testid=favorite-book]').should('have.length.greaterThan', 0)
  })
})

ファイル構成:

src/
  components/
    SearchForm.tsx
    SearchForm.test.tsx
    BookList.tsx
    BookList.test.tsx
  utils/
    search.ts
    search.test.ts
    favorites.ts
    favorites.test.ts
  integration/
    searchFlow.test.ts
  App.tsx
  App.test.tsx

cypress/
  integration/
    bookSearch.spec.js

このような構成で、各コンポーネントや機能に対応するテストファイルを同じディレクトリに配置します。統合テストは別のディレクトリ(integration/)に配置し、E2EテストはCypressの規約に従ってcypress/integrationディレクトリに配置します。

これらのテストを実装することで、アプリケーションの主要な機能や流れを網羅的にカバーし、品質を確保することができます。テストを段階的に追加し、継続的に改善していくことをお勧めします。