マスター Android TDD:テスト駆動開発の実践ガイド
サイマック・マフムーディ著
TDD (テスト駆動開発) は、実際のコードを実装する前にテストを記述するソフトウェア開発アプローチです。
プロジェクト/機能の要件の「何を」「どのように」を明確に理解する必要があります。
TDD を使用すると、作成するコードの量は減りますが、十分な量になります。これは、過剰なエンジニアリング、過剰なテスト カバレッジ、主な要件の欠落、大きすぎる関数やクラス、複雑なコード ステートメントが多すぎるなど、ソフトウェア開発でよくある間違いを防ぐのに役立ちます。
全体として、簡潔で、すでに単体テストがカバーされている、クリーンなコードベースを持つことが役立ちます。時間が経つにつれて、開発コストとコード保守コストも節約されます。
この記事では、実際の TDD について説明します。
コンテキストは Android 開発環境であるため、サンプル プロジェクトで Kotlin と JUnit5 を使用して手順を示します。
ただし、ここで説明する手順とテクニックは他のプログラミング言語でも実践できます。
前提条件
- Kotlin の基礎知識
- 単体テストの作成に関する基本的な知識
- モックとアサーションに関する知識
プログラミング言語として Kotlin を使用し、単体テストの作成には JUnit5 を使用します。
Mockito は、モックとスパイを操作するために使用されます。
対象読者は、キャリアの新たな章を求めているあらゆるプラットフォームのソフトウェア開発者です。
コンテキストは Android ですが、コンテンツはプラットフォーム固有のプロパティについては話していません。代わりに、TDD で開発する際のテクニック、注意事項、課題に焦点を当てます。
上記でよろしければ、始めましょう。
テスト駆動開発の仕組み
TDD サイクル
開発プロセスは次のサイクルに従います。
<オル>失敗するテストの作成 (ピンク色の四角)
このステップでは、コードで何を実行するかを説明することから始めます。
コードが正しく動作するかどうかを確認するテストを行っていると想像してください。このテストは、「このタスクを実行できますか?」など、コードに対して行う質問のようなものです。
最初は、コードは答えを知らないので、コードがタスクの実行方法をまだ知らないため、失敗するはずのテストを作成します。このテストの不合格は、何かが正しくないことを示すピンクの警告サインのようなものです。
この段階が完了すると、JUnit5 は作成したテストから包括的なレポートを生成します。これらのテストは、あなたの作品を具体的に表現したものになります。
ここで、プロジェクト マネージャーがこれらのテスト ケースを読んで、その範囲と、機能または製品に関する理解の正確さの両方を評価していると想像してください。この視点を受け入れることで、この発達段階の重要性がより明確に理解できるようになります。
技術的な複雑さからソフトウェアの動作自体に焦点を移してください。
核心的な技術的な側面に囚われるのではなく、ソフトウェアがどのように機能し、ユーザーや他のコンポーネントとどのように対話するかに注意を向けてください。
この視点の変化により、ソフトウェアの意図した動作と結果に優先順位を付けることができ、実際の動作を正確に反映したテストが可能になります。
技術的な詳細ではなく動作に集中することで、テストがソフトウェアの目的やユーザーの期待と密接に一致していることが保証されます。
場合によっては、コンポーネントあたりのテスト ケースが数個にとどまることもありますが (これが目的です。作業量は少なくなりますが、ターゲットが絞られています)、プロジェクトの動作要件をすべてカバーしている限り、まったく問題ありません。
ヒント
- 具体的にしてください: コードの動作の 1 つの側面に焦点を当てた、明確かつ具体的なテスト ケースを作成する
- シンプルに始める:必要な基本機能をカバーする最も単純なテスト ケースから始めます。
@Test fun `a sum is calculated from two input numbers`() {}
- 意味のある名前を使用する:テストを読んだ人がテストで何をチェックしているのかわかるように、テストにわかりやすい名前を付けます。
@Test fun `Font Ratio is fetched from data source INITIALLY`() {}
避けるべきよくある間違い
- 一度に多すぎるテスト: 1 つのテストで複数のことをテストすることは避けてください。これにより、何が失敗しているのかを特定することが難しくなる可能性があります。
// Don't do this
@Test fun `pixelSize fits the standart sizes while fontSize is bigger than minumum supported font size but matches the list of special levels of size`() {}
- 実装の詳細に依存する: コードの内部動作と密接に結びついたテストを作成しないでください。テストは実装ではなく動作に焦点を当てる必要があります。
// Don't do this
@Test fun `pixelSize is Long and Non-Null and fits the standart sizes then calculated font size is non-null and of type Dimention`() {}
テストに合格するためのコードの実装 (緑色の四角)
テストを準備したので、次はタスクを正しく実行する方法をコードに教えます。
テストに合格する実際のコードを作成すると、そのコードは質問に正しく答えます。
コードがテストに合格すると、「はい、タスクを実行できるようになりました!」という青信号のようなものです。
このステップは、コードが要求している問題を理解し、解決できることを確認することです。
ヒント
- 最小限のコードを作成する:失敗したテストを合格させる最も単純なコードを作成します。オーバーエンジニアリングを回避する方法について詳しくは、こちらをご覧ください。
// Test Case
@Test fun `Storage stores font ratio in key-value`() {
// Given
val fontRatio = 2.0f
val mockEditor = mockk<SharedPreferences.Editor>(relaxed = true)
every { mockSharedPreference.edit() } returns mockEditor
every { mockEditor.putFloat(any(), any()) } returns mockEditor
every { mockEditor.apply() } just Runs
// When
storage.saveFontRatio(fontRatio)
// Then
verify(exactly = 1) {
mockEditor.apply()
}
}
// Correct Implementation - Avoid extra implementation
class SharedPreferenceHelper(
private val sharedPreferences: SharedPreferences
) {
fun saveFontRatio(fontRatio: Float) {
sharedPreferences.edit().putFloat("font-ratio", fontRatio).apply()
}
}
// Wrong Implementation
class SharedPreferenceHelper(
private val sharedPreferences: SharedPreferences
) {
fun saveFontRatio(fontRatio: Float) {
if (fontRatio <= 0.0f)
throw IllegalArgumentException("Font ratio must be greater than 0.0f")
storeValue(key = FONT_RATIO_KEY, value = fontRatio)
}
private fun storeValue(key: String, value: Float){
val editor = sharedPreferences.edit() editor.putFloat(key, value)
editor.apply()
}
fun getFontRatio(): Float {
return sharedPreferences.getFloat("font_ratio", 1.0f) }
}
- 重複を避ける: コードを繰り返さないでください。複数の場所で同様のロジックを作成している場合は、リファクタリングを検討してください。この主なコードの改善はこのフェーズで行うことができますが、変更によって副作用が生じる可能性がある場合は無視してください。
class ... {
override fun getDefaultFontSize(): Float {
val zoomRatio = DEFAULT_SSPEED * DEFAULT_FONT_RATIO / deviceDensity
val fontSize = zoomRatio * standardFontSize
return fontSize
}
override fun getFontSizeBySSpeed(speed: Int): Float {
val zoomRatio = speed * DEFAULT_FONT_RATIO / deviceDensity
val fontSize = zoomRatio * standardFontSize
return fontSize
}
}
class ... {
override fun getDefaultFontSize(): Float = calculate(DEFAULT_AGE)
override fun getFontSizeByAge(age: Int): Float = calculate(age)
private fun calculate(age: Int): Float {
val zoomRatio = age * DEFAULT_FONT_RATIO / deviceDensity
val fontSize = zoomRatio * standardFontSize
return fontSize
}
}
避けるべきよくある間違い:
- 先へ進む:テストに合格するために必要以上のコードを記述しないでください。 TDD は段階的な開発に関するものです。 TDD は、開発に対する段階的かつ段階的なアプローチを奨励します。先に進むということは、基本的に、取り組んでいる現在のテストにまだ直接関係していない問題を解決しようとすることになります。主な目標は、将来の機能に脇道にそれることなく、当面のタスク、つまり現在のテストに合格することに集中することです。
- テストの失敗を無視する:最初にテストが失敗しない場合は、重要なケースを見逃している可能性があります。一見するとこれが起こる可能性は低いように思えますが、テスト コンポーネントをある程度開発した後は、ロジックのさまざまな側面をテストするために 1 つのメソッドに対して複数のテストを作成し始めることになります。ここは、実装されていないロジックがテストに合格したとしても満足すべきではありません。簡単に言えば、これは開発段階でバグを発見する方法です。したがって、失敗するべきときに失敗することを予期してください。
コードのリファクタリング (青い四角) とテストの成功の確認 (薄緑色の四角)
コードがテストに合格したら、クリーンアップを行います。
コードをより整理し、理解しやすく、さらに高速にする方法が見つかるかもしれません。遊び終わったら部屋を片付け、遊び終わった後にすべてをきちんと整理整頓することだと考えてください。コードの動作を変更せずにコードを改善します。
これを行うときは、すべてのテストを実行し続けて、テストが合格することを確認します。このステップでテストが失敗した場合、それは、クリーンアップしたものが誤ってコードを壊した可能性があることを示す、薄緑色の警告サインのようなものです。
この部分は、別のコード保守フェーズとして扱うことができます。
古いコードをクリーンアップし、それがチームのコード品質ガイドラインと製品要件に従っていることを確認するタスクが与えられているとします。
このプロセス全体を通じて、重要なのは、慎重なバランスを維持し、テストが成功し続けることを保証しながら改良することです。
リファクタリング段階のアイデアと戦略は次のとおりです。
- コードの明確さ:複雑なセクションを簡素化し、不明瞭な変数名を置き換え、コメントを強化して、他の人(そして将来の自分)が理解しやすいコードにする
- モジュール性:大きな機能を、焦点を絞った小さな機能に分割します。これにより、コードがよりモジュール化され、メンテナンスやテストが容易になります。
- 冗長性の削除:重複したコードを特定し、再利用可能な関数またはクラスに統合します。これにより、繰り返しがなくなり、一貫性が確保されます。
- 最適化:パフォーマンスを改善できる領域を特定します。ただし、特定のパフォーマンス目標があり、コードがボトルネックであるという証拠がある場合にのみ最適化を行ってください。ここでの最適化は、リソースの浪費を回避することであり、コードのパフォーマンスを向上させることではありません。
- 一貫したフォーマット:チームまたはプロジェクトの規則に従い、一貫したコード スタイルを維持します。
- 未使用のコード:コードベースを乱雑にする未使用の変数、関数、インポートを削除します。
- テストの改善:TDD に関する通常の認識とは異なり、必要に応じていつでもテストを追加できます。新しいテスト ケースを追加して、これまで対処されていなかったシナリオをカバーすることでテスト スイートを強化します。これにより、包括的なテスト範囲を維持することができます。
- ドキュメント:コード自体からコードの目的がすぐに明らかでない場合は、その意図と使用法を説明するドキュメントを追加または改善することを検討してください。習慣化することは避けてください。これは、混乱を避けるために重要なケースを補足的に説明することを目的としています。
TDD コードは自己表現的であり、ドキュメントから独立している必要があることに注意してください。
リファクタリング中は、すべてのテストを実行し続けて、テストが引き続き合格することを確認することが重要であることに注意してください。
ヒント
- テストを包括的に保つ:リファクタリング中に意図しない副作用を検出するために、テストがさまざまなシナリオをカバーしていることを確認してください。
- 段階的にリファクタリングする:コードに小さな変更を加え、テストを頻繁に実行して、リグレッションを早期に発見する
避けるべきよくある間違い:
- テストなしのリファクタリング: テストを実施せずにリファクタリングを行うと、予期しない動作が発生する可能性があります。ロジックの一部を見逃す可能性がある場合は、その部分のテストを作成することを検討してください。
- 大規模なコード変更:場合によっては、テストに合格するために開発した行よりも多くの行を変更することになります。開発段階であまりにも多くの変更を加えるのではなく、別のリファクタリング段階を常に検討してください。これは、より安全でコストの低い選択肢であるためです。
失敗した新しいテストの作成 (フローの再開)
次に、コードに実行させたいことを考えます。
まず、コードが新しいタスクの実行方法をまだ知らないため、失敗するはずの新しいテストを作成します。これは、コードに解決すべき新しい課題を与えるようなものです。
次に、サイクル全体を繰り返します。コードでテストに合格するようにし (緑色の四角)、必要に応じてクリーンアップし (青色の四角)、すべてが機能することを確認するためにテストを続けます (薄緑色の四角)。
こうすることで、常に前進してコードを段階的に構築することができます。
ヒント
- 段階的な手順:新機能の新しいテストを少しずつ追加して、明確な開発パスを維持します。複雑な機能を一度に実装しようとするのではなく、機能をより小さく管理しやすい部分に分割し、これらの部分ごとにテストを作成します。このアプローチにより、明確で安定した開発パスが維持されるため、集中力を維持し、リスクを軽減し、ソフトウェアへの追加要素をすべて徹底的にテストすることができます。
- フィードバック ループ:失敗したテストの作成からのフィードバックを使用して実装をガイドします。フィードバック ループは、TDD の反復的な性質を強調します。新しいテストを作成し、失敗するのを観察すると、実装の指針となる貴重な洞察が得られます。
フィードバック ループの仕組みは次のとおりです。
- 期待値の設定:新しいテストを作成するときは、コードがどのように動作するかについての期待値を定義します。これにより、新機能で何を達成しようとしているのかが明確になります。
- 初期失敗:期待を満たすための対応するコードが欠落しているか不完全であるため、テストは最初は失敗します。この初期の失敗は TDD プロセスの自然な部分です。
- 実装のガイド:テストの失敗からのフィードバックは、どのコードを作成または変更する必要があるかの方向性を示します。これは、新しい機能がどのようなものであるべきかを概説する、開発のロードマップになります。
- 段階的な進捗:テストに合格するために必要なコードを実装すると、必要な機能が段階的に構築されます。各ステップは、失敗したテストによって提供されるフィードバックによって導かれます。
- 検証:実装が完了したら、テストを再度実行します。合格した場合、新しいコードが最初に設定した期待を満たしていることが検証されます。
フィードバック ループにより、開発がソフトウェアの意図した目標と確実に一致するようになります。
避けるべきよくある間違い:
- 実装後のテストの作成:機能の実装後にテストを作成しないでください。 TDD は最初にテストを書くことです。テスト コードの前に追加された小さなロジックであっても、コード内にリソースの無駄やバグが存在する可能性があります。重要なのは、テスト スイートで必要がない限り、ロジックを追加しないことです。
- 失敗したテストをスキップする:機能の実装方法を知っていると思っても、このステップをスキップしないでください。
自信がある場合でも、失敗するテストのステップをスキップすべきではない理由は次のとおりです。
- 意図の明確さ:失敗するテストを作成すると、機能の意図が明確になります。実装に入る前に、目指す正確な動作と結果を検討する必要があります。
- 仮定の検証:機能を理解していると思っていても、テストを作成することで仮定が有効であることが確認されます。あなたの理解は正しいかもしれませんが、テストによってそれが検証されます。
- セーフティ ネット:失敗するテストを作成することで、将来のリグレッションを防ぐセーフティ ネットを確立します。これは機能の仕様として機能し、意図しない副作用を検出するのに役立ちます。
- インクリメンタル開発:TDD はインクリメンタル開発を奨励します。それぞれの新機能は段階的に構築され、失敗したテストから実用的な実装まで明確に進行します。このステップをスキップすると、その進行が中断されます。
- ドキュメント:失敗したテストでは、機能の予想される動作がドキュメント化されます。このドキュメントは、特に将来コードを再検討するときに、あなたとあなたのチームにとって価値があります。プロダクト マネージャーや QA 向けに、すべてのテスト コードをリストしてレポートを生成するシステムがあることを常に忘れないでください。これらのレポートは、あなたが製品内で発見した詳細を明らかにするものであるため、あなたがその点を完全に理解していることを説得するように努めてください。
TDD を使用した開発方法
TDD は、ソフトウェアの設計と開発を推進するために自動テストを作成することの重要性を強調しています。これにより、コードの信頼性と保守性が向上し、時間の経過とともに変更が容易になります。
しかし、どうすればそれを実践できるでしょうか?試してみて、徐々に慣れてください。
新しい機能を開発しながら TDD を試して、実際の世界でどのように使い始めることができるかを実証してみましょう。
フォントサイズの自動設定機能を実装いたします。
ニュース アプリがあり、ユーザーはニュース フィードの自動スクロール速度を設定できます。
ユーザープロフィールページで設定したスクロール速度から画面のフォントサイズを調整する機能を実装したいと考えています。
ユーザーがスクロール速度を 0 から 1 に設定すると、フォント サイズは 1.3 倍になります。
スクロール速度が 1 を超えると、フォント サイズは 1.2 倍になります。
この機能により、ユーザーはニュースを読む際のエクスペリエンスが向上します。
この GitHub リポジトリで調査したコードも共有しました。
自由にクローンを作成して遊んでください。
開発を進めながら、次の手順に従ってください。これにより、TDD における技術や考え方を実践的に理解することができます。
そこで、Android Studio を開いて新しいプロジェクトを作成します。
文字サイズ自動設定機能データフローチャート
Android アプリのサンプル機能の DFD
上は、データ フローが最終的にどのようになるかを示したフローチャートです。
魅力的な各コンポーネントの概要は次のとおりです。
AutoScrollSettingsUseCase クラスは、FontRatio を計算して保存するためのロジックを処理します。 選択したスクロール速度に基づきます。
このユースケースは UserRepository に依存します。 FontRatio を保存しています 値。
UserRepository で 、FontRatio を保存および取得するメソッドがあります。 Storage を使用した値 仕組み。新しい FontRatio が追加されるたびに がストレージに送信されると、オブザーバブルは最新の値を持つエミッションを受け取ります。
UserProfileViewModel で 、AutoScrollSettingsUseCase のインスタンスがあります。 ユーザーがスクロール速度を更新するたびに呼び出されます。これにより、FontRatio の再計算がトリガーされます。 そしてリポジトリ経由で保存されます。
ユーザー設定セクションには、ユーザーが希望のスクロール速度を入力できるようにするために必要な UI コンポーネントが用意されています。これは、NumberPicker などの標準の Android UI 要素を使用して実行できます。 またはカスタム UI コンポーネント (これらの部分については説明しません)。
これは単純な機能の内訳であり、実行するたびに、手順と最終結果がより明確になります。変更を加えるためにこれを行うことが重要です。
テストの書き方
最初のステップは、常にテスト クラス自体を作成することです。この場合、少なくとも次のテスト クラスが存在します。
UserRepositoryTestAutoScrollSettingTestUserSettingsViewModelTest
ViewModel 部分から始めることをお勧めします。
ViewModel は、ライフサイクルの変更 (前景、背景、フォーカスなど) を回避する Android アーキテクチャ コンポーネントです。したがって、州を保存するのに最適な場所です。
unitTest 内にテスト ファイルを作成しましょう。 実際の機能コードと同じパッケージ パスに続くソース コードのディレクトリ。
実際の TDD は、従来の作業開発とは異なります。
TDD では、IDE を使用してファイルとプロパティ (フィールド) の作成プロセスを高速化します。ただし、テスト ファイルは手動で作成します。何度か試してみると、IDE のこの側面が便利になるでしょう。
コード構造 (パッケージ) を作成し、パッケージを右クリックして Class を選択してテスト クラスを作成します。 タイプ。
わかりやすい名前を付けてください (規則にまだ従っていない場合は、従う必要があるかもしれません)。
例:xxx is tested for 、xxx はテストされるコンポーネントの名前です。
IDE を使用してファイルを作成する
次に、空のテストを作成しましょう。できるだけ幅広いものにするようにしてください。
図によれば、この機能には多くのロジックはありません。
単体テスト関数を作成するには、主に 2 つの戦略があります。
- AAA
- 与えられた/いつ/それから
@Test fun `strategy A`(){
// Arrange
// Act
// Assert
}
@Test fun `strategy B`(){
// Given
// When
// Then
}
1 つを選択し、すべてのテストでそれに従いましょう。
コンセプトは同じです。テスト コードをクラスタ化すると、読みやすく、保守しやすくなります。
現時点で私が持っているものは次のとおりです。
class UserProfileViewModel is tested for` {
// Unimplemented Class
val viewModel = UserProfileViewModel()
@Test
fun Font Ratio is fetched from data source`(){}
@Test
fun `Scroll Speed update is called so fontSize calculations are triggered`() {}
@Test
fun `Font Ratio is updated with new emissions from data source`() {}
}
テストを実行しましょう!
テスト ターゲットが欠落しているためテストに失敗しました
失敗してるよ。実際、テストではなくビルドが失敗しました。
おめでとうございます! TDD サイクルの最初のステップに到達したところです。
TDD サイクルの最初のステップ
ViewModel がまだ存在しないため、赤色になっています。
次に、ViewModel のインスタンスを作成しましょう。
そのため、IDE を使用して、欠落しているクラスまたは未実装のコードを作成します。
このダイアログをポップアップさせるには、ポインタを未実装の部分に移動し、Option + Return キーを押します (macOS の場合)。
次に、提供されたオプションに従います。
TDD アクション:UnitTest ファイルを使用してターゲット ファイルを作成する
新しいファイルの正しい保存先を選択してください
そして、テストを再実行しましょう (最後のステップ):
テストの合格を表示
はい!合格しました。
これらのテストの本体は空であり、何もテストしないことに注意してください。それは正しいですし、大丈夫です。
DFD 図内のすべてのコンポーネントに対して、すべてのテスト クラス (まだ空のテスト本体を含む) を作成し続ける必要もあります。これらについては記事の冒頭で共有しました。
これは、機能を実装する前に機能をより明確に理解するのにさらに役立ちます。
最終的には、一般的なシナリオと単体テストを含む 3 ~ 4 つのテスト クラスをカバーすることになります。
次のようになります:
最小限の空のテスト ケース
例としてそのうちの 1 つを実装してみましょう。
ただし、その前に、この機能の UI およびドメイン データ モデルを操作する必要があります。
したがって、データを移動できるようにするために、必要なデータ クラスを事前に作成しましょう。
ProfileViewModel に戻る テストクラスには、空の単体テスト関数があります。それを実装しましょう。
ここで重要なのは、テストを注意深く読み、余分な実装やアサーションを避けることです。
要件のみ実装が許可されます。
この場合、以前に作成したデータ ソース (UserRepository) に接続されたデータ ストリームが必要です。 )。
忘れないでください:まず失敗するテストが必要です。
内部ボディを実装する
テスト関数本体内の未実装の部分 (赤いフォントでマークされている) に注目してください。
次に、コードを実装し、それが通過するようにリファクタリングしましょう。
ここでは、クラスとオブジェクトをモックするために MockK ライブラリを使用し、フロー ストリームをテストするために Turbine を使用しています。
慣れていなくてもパニックにならないでください。公式ウェブページをチェックして試してみてください。
まず依存関係を作成し、名前付き引数を使用して ViewModel に追加しましょう。名前付き引数は、IDE 経由でパラメータを作成するときに、テスト コードを通じて適切な名前を導入するのに役立ちます。
IDE ダイアログを使用して欠落パラメータを作成する
FontRatio に対しても同じことを行います。 リポジトリ内の変数。
最終的に、最終的なテスト コードは次のようなコードになります。
class `UserProfileViewModel is tested for` {
init {
Dispatchers.setMain(Dispatchers.Unconfined)
}
val mockUserRepository = mockk<UserRepository>()
@Test
fun `Font Ratio is fetched from data source`() = runTest {
// Given
val expectedRatio = 2.0f
every { mockUserRepository.fontRatio } returns flowOf(FontRatioUiModel(expectedRatio))
val viewModel = UserProfileViewModel(userRepository = mockUserRepository)
// When
viewModel.fontRatio.test {
val fromDataSource = expectItem()
// Then
assertEquals(/* expected = */ expectedRatio, /* actual = */ fromDataSource.fontRatio)
}
}
...
}
ここでは ViewModel または Repository の内部部分を実装していないことに注意してください。
不足している部分を作成してテスト本体からエラーを削除するだけです。
これらの詳細は次のイテレーションで実装します。
今すぐテストを実行してください。
FontRatio を実装していないため、もちろん失敗します。 ProfileViewModel 内 。
次に、ViewModel をリファクタリングしてテストに合格します (最小限の実装)。
この場合、状態フローをリポジトリに接続するだけで済みます。前回の反復ですでに依存関係として追加しました。
最終的なコードは次のとおりです。
class UserProfileViewModel(
userRepository: UserRepository
) : ViewModel() {
val fontRatio: StateFlow<FontRatioUiModel> = userRepository.fontRatio.stateIn(
initialValue = FontRatioUiModel(DEFAULT_FONT_RATIO),
scope = viewModelScope,
started = SharingStarted.Lazily
)
companion object {
private const val DEFAULT_FONT_RATIO = 1.0f
}
}
もう一度テストを実行すると、ドーン!合格です!
メインコードの最小限の実装後にテストに合格
この単体テストでは、UserProfileViewModel の主要な部分を実装しました。 コンポーネント。ただし必要な部分のみ。残りのテスト ケースについても同じことを行います。
これらのテスト ケースを、通常の単体テスト (すぐに実行されて通過する) と同じように扱わないでください。
まずは時間をかけて技術要件と製品要件を理解してください。次に、計画を展開し、実装を開始します。数回試してみると、TDD の考え方がより簡単になるでしょう。
ソースコード
このプロジェクトのソース コードとリポジトリは、私の GitHub ページから入手できます。
ぜひご確認いただき、次のステップを完了してください。独自の実装と比較できるように、反復を異なるブランチに分けました。
結論
したがって、テスト駆動開発 (TDD) に飛び込み、その隅々まで調査した結果、これはゲームチェンジャーであると言わざるを得ません。
簡単に説明しましょう:
重要なポイント:
- TDD とは、実際のコードを書く前にテストを書くことです。最初は少し奇妙に聞こえるかもしれませんが、信じてください、これは驚くべき効果を発揮します。
- TDD プロセスは単純なサイクルに従います。つまり、失敗したテストを作成し、それらのテストを合格させるコードを実装し、すべてをスムーズに実行し続けるために必要に応じてリファクタリングします。
- TDD は自動テストを重視することで、堅牢で保守性が高く、長期にわたって適応できるソフトウェアの設計と開発に役立ちます。
私たちはテスト クラスを作成し、空のテスト関数を記述することから始めましたが、それを完了するのはあなたの仕事です (または、すぐに共有リポジトリにジャンプすることもできます :) )。
何も行わないテストがあるのは少し奇妙に思えるかもしれませんが、それはすべて計画の一部です。
次に、データ フロー チャートに基づいて、機能が何をすべきか明確な計画を立てました。これは、実装に入る前に要件を理解するのに役立ちました。
計画を手に入れたので、必要なコンポーネント (この場合は ViewModel) の実装を開始し、最初にテストが失敗することを確認しました。そうです、TDD ではテストの失敗は実際には良いことなのです!
UserAutoScrollSettingsUseCase をセットアップするなど、徐々にピースを接続していきました。 自動スクロール速度に基づいてフォント サイズの計算を処理するクラス (プロジェクト リポジトリを確認してください)。
また、UI コンポーネントにも取り組み、ユーザーが希望のスクロール速度を入力できるようにし、それに応じてフォント サイズが調整されるようにしました (プロジェクト リポジトリを確認してください)。
プロセス全体を通じて、テストに合格するために必要なものに重点を置き、コードをクリーンかつシンプルに保つようにしました。ここには不必要な複雑さはありません!
最終的には、「フォント サイズの自動設定」機能が稼働し、テストも見事に合格しました。
TDD は、テストやコーディングを狂ったように急いで行うことではないことを忘れないでください。開発プロセスにおいて慎重かつ思慮深く取り組むことが重要であり、それは長期的には大きな成果をもたらします。
したがって、ソフトウェア開発ゲームをレベルアップしたい場合は、TDD を試してみてください。これは、コードをより堅牢にし、バグを減らし、全体的に優れた開発者になれる強力なアプローチです。
私が共有したのは、私たちがチームでどのように仕事をしており、それがうまく機能しているかということですが、それがすべてのチーム/企業にとって完璧なソリューションではないということです。それが自分のものかどうかを調べる必要があります。このソリューションを改善できると思われる場合は、お知らせください。
コーディングを楽しんでください! 🚀
無料でコーディングを学びましょう。 freeCodeCamp のオープンソース カリキュラムは、40,000 人以上の人々が開発者としての職に就くのに役立ちました。始めましょう
-
2017年のAndroid向け13ベストルートアプリ
スマートフォンをルート化すると、Playストア内(場合によってはそれ以外)の多数の追加アプリにアクセスできるようになります。最高のルートアプリは、バッテリー寿命を延ばし、フォントや絵文字を変更し、削除された写真を復元し、Linuxのフルバージョンを起動することさえできます。 インストールする必要のある13の最高のルートアプリを以下に示します。 1.ルートエッセンシャル Root Essentialsは、多数のルートタスクのワンストップショップです。これは、build.propエディター、ルートエクスプローラー、フラッシュツールなどの重要なツールが含まれているため、root化を初めて使用する
-
Android の省エネを実現:AOSP の低電力アイランドにおける Bluetooth ソケット設定の役割
これを想像してみてください。あなたはカフェに座っていて、ラップトップを開いて、テーブルの上に携帯電話を置き、数分ごとにスマートウォッチが鳴り、Bluetooth イヤホンで音楽を再生しているとします。あなたから見ると、人生は平和です。携帯電話の観点から見ると、携帯電話は常に膨大な数の小さな Bluetooth パケットを処理していることになります。 時計が歩数を同期するたび、イヤホンが別の音声を受信するたび、バックグラウンド デバイスがチェックインするたび、携帯電話内のメイン アプリケーション プロセッサが強制的に起動し、データを確認し、そのデータをどう処理するかを決定し、その後スリープに戻