0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Manusさんってすごいかもしれない?

Posted at

最近のネットニュースで新AiのManusがすごいみたいな記事が出ていたので、先週末にアカウントを取得しに行きました。みた
ニュースでは招待コードが発行してもらうのが難しいということが記載されていたので、だめもとで申請したらあっさりアカウントが発行されました。
そんなわけで、ちょっとだけ使ってみました。

Manusって何?という人向けに、どなたかが書いた記事がありますので、ご参考としてください。

Manusにやってもらったこと

練習で作ったBookshelfというアプリのViewModelをManusに読み込ませて、テストコードを提案してと投げました。

image.png

Manus仕事をする

Manusが何やら作業をし始めました。
具体的にManusが今やっていることがログに表示されます。
image.png

Manus結果を出す

時間がかかるかもしれないと思い、別のことをやっていたのですが、
10分程度でテストコードを作成しました!と通知が来たのでのぞいてみると。。。

以下Manusの出力の画面ショットです。
image.png

image.png

READMEにはテストの実装について詳細に記載がありました。
READMEはmdで記載されているので、そのままコピペします。
ぱっと見ですが、変なところもあるのでそのまま使えないかもしれませんが、とても参考になるのではないかと感じました。

なお、提案いただいたテストコードは未検証です!!!!
検証は今週末にしようと思ってます!

↓↓↓↓↓↓ 以下manusが書いたREADME ↓↓↓↓↓↓

BookshelfViewModel テストガイド

このドキュメントでは、BookshelfViewModelのユニットテストの実装方法と実行方法について説明します。

テスト概要

BookshelfViewModelは、GoogleBooksAPIから本のデータを取得し、UI状態を管理するViewModelです。
主な機能は以下の通りです:

  • fetchBooks(): 指定したクエリで本を検索する
  • setBookId(): 選択した本のIDを設定する
  • NetworkUiState: 通信状態を管理するシールドクラス

テストでは、これらの機能が正しく動作することを確認します。

必要な依存関係

テストを実行するには、以下の依存関係をプロジェクトのbuild.gradleファイルに追加する必要があります:

dependencies {
    // 既存の依存関係
    
    // テスト用の依存関係
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.mockito:mockito-core:4.0.0'
    testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0'
    testImplementation 'androidx.arch.core:core-testing:2.1.0'
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
}

テストの実装

テストはBookshelfViewModelTest.ktファイルに実装されています。テストクラスは以下の主要なセクションで構成されています:

  1. テスト環境のセットアップ
  2. 基本機能のテスト
  3. エッジケースのテスト

テスト環境のセットアップ

テストでは、以下の環境を設定しています:

  • InstantTaskExecutorRule: LiveDataの非同期操作を同期的に実行するためのルール
  • テスト用のCoroutineディスパッチャー: コルーチンのテストを制御するためのディスパッチャー
  • モックリポジトリ: 実際のAPIリクエストを行わずにテストするためのモック
@ExperimentalCoroutinesApi
class BookshelfViewModelTest {
    @get:Rule
    val instantTaskExecutorRule = InstantTaskExecutorRule()

    private val testScheduler = TestCoroutineScheduler()
    private val testDispatcher = StandardTestDispatcher(testScheduler)

    private lateinit var mockRepository: BooksRepository
    private lateinit var viewModel: BookshelfViewModel

    @Before
    fun setUp() {
        Dispatchers.setMain(testDispatcher)
        mockRepository = mock(BooksRepository::class.java)
    }

    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }
    
    // テストメソッド...
}

基本機能のテスト

基本機能のテストでは、以下の項目を確認しています:

  1. 初期化時の動作
  2. fetchBooks()の成功、空結果、エラー時の動作
  3. setBookId()の有効なID、無効なID、異なる状態での動作

例えば、fetchBooks()の成功時のテストは以下のようになります:

@Test
fun `fetchBooks成功時にSuccessステトに遷移する`() = runTest {
    // モックの動作を設定
    `when`(mockRepository.getBooks(any())).thenReturn(
        BooksApiResponse(items = dummyBookList)
    )

    // ViewModelを初期化
    viewModel = BookshelfViewModel(mockRepository)
    testScheduler.advanceUntilIdle()
    
    // 新しいクエリでfetchBooksを呼び出し
    viewModel.fetchBooks("Kotlin")
    
    // コルーチンの実行を待機
    testScheduler.advanceUntilIdle()
    
    // 状態がSuccessに変わったことを確認
    val currentState = viewModel.uiState.value
    assert(currentState is NetworkUiState.Success)
    assertEquals(dummyBookList, (currentState as NetworkUiState.Success).bookList)
    
    // リポジトリのgetBooksメソッドが呼ばれたことを確認
    verify(mockRepository).getBooks("Kotlin")
}

エッジケースのテスト

エッジケースのテストでは、以下の項目を確認しています:

  1. 空のクエリでのfetchBooks()呼び出し
  2. fetchBooks()の連続呼び出し
  3. ネットワークエラー時の挙動
  4. 同じIDでの連続setBookId()呼び出し
  5. fetchBooks()後のsetBookId()呼び出し

例えば、fetchBooks()の連続呼び出しのテストは以下のようになります:

@Test
fun `fetchBooks連続して呼び出した場合に最新の結果が反映される`() = runTest {
    // 1回目と2回目の呼び出し用のモック設定
    val firstBookList = listOf(/* ... */)
    val secondBookList = listOf(/* ... */)
    
    `when`(mockRepository.getBooks("first")).thenReturn(
        BooksApiResponse(items = firstBookList)
    )
    `when`(mockRepository.getBooks("second")).thenReturn(
        BooksApiResponse(items = secondBookList)
    )

    // ViewModelを初期化
    viewModel = BookshelfViewModel(mockRepository)
    testScheduler.advanceUntilIdle()
    
    // 1回目のfetchBooks呼び出し
    viewModel.fetchBooks("first")
    testScheduler.advanceUntilIdle()
    
    // 1回目の結果を確認
    val firstState = viewModel.uiState.value
    assert(firstState is NetworkUiState.Success)
    assertEquals(firstBookList, (firstState as NetworkUiState.Success).bookList)
    
    // 2回目のfetchBooks呼び出し
    viewModel.fetchBooks("second")
    testScheduler.advanceUntilIdle()
    
    // 2回目の結果を確認(最新の結果が反映されていること)
    val secondState = viewModel.uiState.value
    assert(secondState is NetworkUiState.Success)
    assertEquals(secondBookList, (secondState as NetworkUiState.Success).bookList)
}

テストの実行方法

テストを実行するには、以下の方法があります:

  1. Android Studioから実行する場合:

    • テストクラスを開き、クラス名の横にある実行ボタンをクリックする
    • または、個々のテストメソッドの横にある実行ボタンをクリックする
  2. コマンドラインから実行する場合:

    ./gradlew testDebugUnitTest
    

テスト実行時の注意点

  1. テストはJVMで実行されるため、Androidフレームワークに依存するコードはモック化する必要があります。
  2. Coroutinesのテストでは、runTestブロック内でコルーチンを実行し、testScheduler.advanceUntilIdle()を呼び出して非同期処理の完了を待つ必要があります。
  3. @ExperimentalCoroutinesApiアノテーションは、Coroutinesのテスト関連APIが実験的であることを示しています。将来のバージョンで変更される可能性があります。

テストカバレッジ

このテストコードは、BookshelfViewModelの以下の機能をカバーしています:

  • 初期化時の動作
  • fetchBooks()の正常系と異常系
  • setBookId()の正常系と異常系
  • 様々なエッジケース

テストカバレッジを確認するには、Android Studioの「Run with Coverage」機能を使用するか、以下のGradleコマンドを実行します:

./gradlew testDebugUnitTestCoverage

今後の改善点

  1. UIテストの追加:Composeを使用したUIとの連携テスト
  2. 統合テスト:実際のリポジトリとの統合テスト
  3. パフォーマンステスト:大量のデータを扱う場合のパフォーマンステスト
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?