【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)
この記事からわかること
- Android Studio/Kotlinで使えるJUnitの使い方
- テスト駆動開発とは?
- 単体テストの実装方法
- アノテーションの種類
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Android Studio:Koala
- Kotlin:1.9.0
- JUnit:4
JUnitとは?
公式リファレンス:JUnit
公式リファレンス:JUnit4リポジトリ
JUnitはJavaアプリケーションの単体テストを記述、実行するためのフレームワークです。JUnit自体はテスト駆動開発(TDD)と呼ばれる開発手法をサポートするためのツールとして開発されたもので、テストケースの定義やテストの自動実行などテストを行う上で必要な機能が色々用意されています。
単体テストとは?
単体テスト(Unit Test)とは個々のユニット(メソッド、関数、クラスなど)が期待どおりに動作するかどうかを確認するためのテスト方法です。実装した処理自体が間違っていないか、異なる結果を出力しないかを個々にチェックすることが目的になります。
例えば以下のようなテストを行います。
- 金額の計算をするメソッドの結果が期待通りかどうか
- ファイルからデータを読み込む関数が正しく動作するか
- クラスのイニシャライザが期待通りに動作するか
単体テストを実装する上でモックを作成することも多いと思いまずがMockK
というモックライブラリを使用すると便利です。
テスト駆動開発(TDD)とは?
テスト駆動開発(TDD)とはプログラムを書く前にテストコードを書く手法のことです。テスト駆動開発では「Red・Green・Refactoring」という3つのステップをサイクルとして取り入れて開発を進めていきます。
- Red:テストコードを書く
- Green:実装コードを書く
- Refactoring:コードを洗練させる
このサイクルを繰り返すことによりコードの品質を保ちながら開発を進めることが可能になります。
メリット
- 品質の向上
- デグレの防止
- 仕様の明確化
デメリット
- 開発工数の増加
- テストコードを記述するスキルが必要
導入方法
Android StudioでJUnitを導入するためには「build.gradle(Module:app)」にtestImplementation 'junit:junit:4.13.2'
を記述します。とはいえAndroid Studioでプロジェクトを立ち上げると最初から導入されているかと思うので明示的に導入する機会は少ないかもしれません。
他にもデフォルトで色々入っているようです。
dependencies {
// 単体テスト
testImplementation 'junit:junit:4.13.2'
// インストルメンテーションテスト(デバイス上またはエミュレータ上で実行するテスト)
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
// UIテスト
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
現在デフォルトで導入されるのはJUnit4ですが、現時点の最新はJUnit5のようです。
JUnitの使い方
JUnitを使用して単体テストを実装する流れを見ていきます。テストコードはViewModel
やManager
クラスなどの単位で作成していくと管理しやすいです。今回はシンプルなSampleManager
クラスを用意しています。メソッドはaとbを足すadd
とaをbで割るdivide
の2つが定義されています。
class SampleManager {
/** aとbを足す */
fun add(a: Int, b: Int): Int {
return a + b
}
/** aをbで割る */
fun divide(a: Int, b: Int): Int {
if (b == 0) throw IllegalArgumentException("Cannot divide by zero")
return a / b
}
}
テストコードクラスの自動生成
テストコードクラスはAndroid StudioのGUI操作で自動で生成することが可能です。まずは対象のクラスの上で右クリック>「Generate...」をクリックします。
続いて「Test...」をクリックします。
Testing Library
をJUnit4
に変更してテスト対象のメソッドをにチェックを入れておきます。
最後に生成先を指定して「OK」をクリックします。
(test)ディレクトリの中に対象のテストコードクラスが自動生成されます。
この時点で生成されたテストコードクラスは中身がまだ空の状態です。
class SampleManagerTest {
@Test
fun add() {
}
@Test
fun divide() {
}
}
単体テストコードを記述する
単体テストコードの記述するポイントは以下の通りです。
- @Testアノテーションを付与する
- assertEqualsなどを使用して検証する
テストを実行するためにSampleManager
クラスをインスタンス化してプロパティとして保持しておき各テストメソッドの中で対象のメソッドに対してassertEquals
を使用して期待通りの値になっているかを検証します。
import org.junit.Assert.*
import org.junit.Test
class SampleManagerTest {
private val manager = SampleManager()
@Test
fun testAdd() {
val result = manager.add(2, 3)
assertEquals(5, result)
}
@Test
fun testDivide() {
val result = manager.divide(10, 2)
assertEquals(5, result)
}
/** 例外を発生させるテスト */
@Test(expected = IllegalArgumentException::class)
fun testDivideByZero() {
manager.divide(10, 0)
}
}
テストの実行
テストを実行するには対象クラスを右クリック>「Run 'テストクラス名'」をクリックします。テストが実行され異常なく成功すると各テストメソッドの左側に緑色のマークが付与されます。(失敗すると赤色のマーク)
複数テストクラスがある場合は以下を実行することでテストを実行可能です。
$ ./gradlew test
Fastlaneなどを使用して自動化することも可能になります。
ログを仕込む
テストケースないから何かしらの値を出力したい場合はprintln
を使用することで値を出力することができます。
@Test
fun testAdd() {
val result = manager.add(2, 3)
println("result $result")
assertEquals(5, result)
}
出力した値は「Run」タブから確認することができます。
アノテーション
JUnit4で使用できるアノテーションの種類と使い方をまとめていきます。
@Test
テストケースを定義するために付与するアノテーション。これを付与しないとテストコードとして認識されない。また例外を検証するには@Test(expected = Exception.class)
とする。
@Test
fun testDivide() {
val result = manager.divide(10, 2)
assertEquals(5, result)
}
/** 例外を発生させるテスト */
@Test(expected = IllegalArgumentException::class)
fun testDivideByZero() {
manager.divide(10, 0)
}
@Before
各テストの実行前に共通の初期化処理を実装するために付与するアノテーション。各テストケース実行前に呼ばれるので変数の初期化などに活用する。
import org.junit.Before
class ExampleTest {
private var x: Int = 0
@Before
fun setUp() { x = 5 }
}
@After
各テストの実行後に共通のクリーンアップ処理を実装するために付与するアノテーション。各テストケース実行後に呼ばれるので変数の初期化などに活用する。
import org.junit.After
class ExampleTest {
@After
fun tearDown() {
println("Test complete.")
}
}
@BeforeClass
テストクラス全体で1回だけ実行する初期化処理を実装するために付与するアノテーション。対象メソッドは静的メソッド(@JvmStaticを指定してcompanion object)にする必要がある。
import org.junit.BeforeClass
class ExampleTest {
companion object {
@BeforeClass
@JvmStatic
fun setUpClass() {
println("Test class setup.")
}
}
}
@AfterClass
テストクラス全体で1回だけ実行するクリーンアップ処理を実装するために付与するアノテーション。対象メソッドは静的メソッド(@JvmStaticを指定してcompanion object)にする必要がある。
import org.junit.AfterClass
class ExampleTest {
companion object {
@AfterClass
@JvmStatic
fun tearDownClass() {
println("Test class complete.")
}
}
}
@Ignore("理由")
指定したテストをスキップするために付与するアノテーション。
import org.junit.Ignore
import org.junit.Test
class ExampleTest {
@Ignore("このテストは対象外とする")
@Test
fun testExample() { }
}
アサーションメソッドの種類
検証するためのアサーションメソッドもassertEquals
だけでなく色々用意されています。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。