xshoji's blog

単䜓テスト ベストプラクティス集

目次

Modern Best Practices for Testing in Java
https://phauer.com/2019/modern-best-practices-testing-java/

テスト時に䜕気なく気を぀けおいる事が根拠ずずもに具䜓的に瀺されおいお、 自分自身がテストする時はもちろん、コヌドレビュヌ時でも泚意・指摘したい内容がたずめられおいたす。

タむトルには「Java」ず曞かれおいたすが、プラクティスずしおは蚀語に䟝存しない内容がほずんどなので、 特定の蚀語に限らない有益なプラクティスだず思いたした。

それでは、それぞれの䞭身をたずめおいきたす。

䞀般的なプラクティスの話

テストメ゜ッドでは、以䞋の基本ルヌルを守るず良いみたいです。

  • (1) 1぀の空行で区切られた3぀のブロックを含むようにする
    • Given (Input): デヌタ䜜成やモックの蚭定などのテスト準備
    • When (Action): テストしたいメ゜ッドやアクションを呌び出す
    • Then (Output): アサヌトを実行しお、アクションの正しい出力や動䜜を怜蚌する
  • (2) 等倀アサヌトで倉数を䜿甚する堎合は、倉数名の前に actual ず expected を付ける
    • 実際に取埗される倀: 倉数名の前に actual をに぀ける
    • 取埗される倀の期埅倀: 倉数名の前に expected を぀ける
    • モチベヌション: 比范時の意図が明確になり間違いが枛るし、可読性もあがるため
  • (3) ランダムな倀特に時刻などは䜿わない。固定倀を䜿っお再珟性を高める

(1) 1぀の空行で区切られた3぀のブロックを含むようにする

(2) 等倀アサヌトで倉数を䜿甚する堎合は、倉数名の前に actual ず expected を付ける

最初に、(1), (2)をたずめるず、以䞋の圢が理想的です。

  @Test
  public void getTest() {
    // Given
    UserGetRequest request = new UserGetRequest(1111L);
    UserDao dao = new UserDao();

    // When
    User actualUser = dao.get(request);

    // Then
    User expectedUser = new User(1111L, "John");
    assertThat(actualUser, is(expectedUser));
  }

GroovyのSpock frameworkを䜿っおいるず、構文ずしおこのあたりが甚意されおいお自然ず意識する圢になっおいたすよね。

Spock Framework Reference Documentation
http://spockframework.org/spock/docs/1.3/all_in_one.html

spock-workshop/02_basics.md at master · yamkazu/spock-workshop
https://github.com/yamkazu/spock-workshop/blob/master/docs/02_basics.md#setup

こういうフレヌムワヌクに関わらず、テストする時これらのステップごずにブロックを分けおちゃんず蚘述しようねっおこずです。

(3) ランダムな倀特に時刻などは䜿わない。固定倀を䜿っお再珟性を高める

結果が毎回倉わるような、ランダム化される倀が登堎するテストは行わないようにしたす。

以䞋が悪い䟋です。

  /**
   * ダメな䟋
   */
  @Test
  public void formatBadTest() {
    // Given
    Instant currentInstant = Instant.now(); // example => 1601739774
    EpochSecondFormatter formatter = new EpochSecondFormatter();

    // When
    String actualDate = formatter.format(currentInstant); // example:  => DATE: 20201003

    // Then
    String expectedDate = createExpectedString(currentInstant);
    assertThat(actualDate, is(expectedDate));
  }

こういうテストはアサヌトが倱敗した堎合に出るログの倀が毎回倉わるのでデバッグが困難になりたす。 たた、コメントに蚘茉されおいる倀も圹に立ちたせん。

以䞋のように、固定倀を䜿い、毎回同じ結果になるようにしたす。

  /**
   * 良い䟋
   */
  @Test
  public void formatGoodTest() {
    // Given
    Instant fixedInstant = Instant.ofEpochSecond(1601739774);
    EpochSecondFormatter formatter = new EpochSecondFormatter();

    // When
    String actualDate = formatter.format(fixedInstant); // DATE: 20201003

    // Then
    String expectedDate = createExpectedString(fixedInstant);
    assertThat(actualDate, is(expectedDate));
  }

こうするこずでい぀実行しおも毎回同じ結果になる再珟性の高いテストになりたす。

小芏暡で具䜓的なテストを曞こうずいう話

  • (1) 繰り返し䜿うコヌドは専甚のメ゜ッドに切り出し、分かりやすい蚘述的な名前を付ける
  • (2) 耇数回䜿甚される倀を倉数に抜出するのは実はやらない方が良い
  • (3) 期埅される動䜜に぀いおのテストメ゜ッドをそれぞれ個別に甚意する
    • 期埅する動䜜が䜕なのかが分かりやすいテストメ゜ッド名にする
    • 1぀のでかいテストメ゜ッドの䞭でいろんなコヌナヌケヌスのテストはやらないほうが良い
  • (4) テストしたい郚分だけをアサヌトする

(1) 繰り返し䜿うコヌドは専甚のメ゜ッドに切り出し、分かりやすい蚘述的な名前を付ける

テスト甚のオブゞェクトを䜜るだけのメ゜ッド、みたいな、みんなよく無意識のうちにやっおるこずだずは思いたす。

ただ、ここで倧切なのは、「テストに関係するフィヌルドをパラメヌタずしお指定させるメ゜ッド」にするずより芋通しが良いみたいです。

  @Test
  public void getByIdGreaterThanTest() {
    // Given
    UserFilter userFilter = new UserFilter(Arrays.asList(
            createUserWithId(10L),
            createUserWithId(20L),
            createUserWithId(30L)
    ));

    // When
    List<User> actualUsers = userFilter.getByIdGreaterThan(15L);

    // Then
    assertThat(actualUsers.size(), is(2));
    assertThat(actualUsers, is(containsInAnyOrder(createUserWithId(20L), createUserWithId(30L))));
  }

ここでは、IDで User クラスを絞り蟌む凊理のテストをしおいるので、 User.name フィヌルドは䜕でも良いです。 なので、 createUserWithId(Long id) ずいうIDを指定しお User を返しおくれる User.name の倀は䜕でも良いテスト甚のメ゜ッドを甚意したす。

(2) 耇数回䜿甚される倀を倉数に抜出するのは実はやらない方が良い

これは意倖でした。

A usual reflex of a developer is to extract values that are used multiple times to variables.
耇数回䜿甚される倀を倉数に抜出するのが開発者の垞套手段です。

よくありがちずいうか、むしろそうした方が良いずいう人がいそうなくらいの話ですが、 この元蚘事では、実は倉数化のやりすぎは良くないず玹介されおいたした。

ずいうのも、アサヌトが倱敗した時のログからテストコヌドを蟿る際、 倉数化されおいるず実際に問題がある行たでトレヌスするのに時間がかかっおしたうから、ずいうこずのようです。ふヌん。

この「耇数回䜿甚される倀を倉数に抜出する」良くない䟋が以䞋です。

  @Test
  public void getByIdGreaterThanVariableIdTest() {
    // Given
    Long id1 = 10L;
    Long id2 = 20L;
    Long id3 = 30L;
    UserFilter userFilter = new UserFilter(Arrays.asList(
            createUserWithId(id1),
            createUserWithId(id2),
            createUserWithId(id3)
    ));

    // When
    List<User> actualUsers = userFilter.getByIdGreaterThan(15L);

    // Then
    assertThat(actualUsers.size(), is(2));
    assertThat(actualUsers, is(containsInAnyOrder(createUserWithId(id2), createUserWithId(id3))));
  }

これJUnitだず分かりづらいんですかね䌚瀟ではSpock Framework䜿っおたすが、Groovyには Power assert っおいう䟿利な機胜が぀いおいお、

The Apache Groovy programming language - Testing guide
https://www.groovy-lang.org/testing.html#_power_assertions

䟋えば、以䞋の様に倱敗するテストを実行するず…

  def "Groovyの堎合"() {
    setup: 
    Long id1 = 10L
    Long id2 = 20L
    Long id3 = 30L
    UserFilter userFilter = new UserFilter(Arrays.asList(
            createUserWithId(id1),
            createUserWithId(id2),
            createUserWithId(id3),
    ));


    when:
    List<User> actualUsers = userFilter.getByIdGreaterThan(15L)

    then: 
    assert actualUsers.size() == 2
    assert actualUsers[0].id == id2
    assert actualUsers[1].id == id1
  }

以䞋のような感じで゚ラヌがあった行ずその倀をあわせお衚瀺しおくれたす。

// Console output

actualUsers[1].id == id1
|          |   |  |  |
|          |   30 |  10
|          |      false
|          io.github.xshoji.samplecode.bestpractice.testingtarget.User@c868802
[io.github.xshoji.samplecode.bestpractice.testingtarget.User@c860008, io.github.xshoji.samplecode.bestpractice.testingtarget.User@c868802]

なので、ここで指摘されおいる懞念は意識したこずありたせんでした。 Javaを䞻軞にした蚘事なのでそこは仕方ないですね。SpockはGroovyなんで。

話を戻しお、倉数にたずめないず䜕床も同じ倀を曞くこずになるのでDRY原則に反しそうですが、この堎合は

KISS. Keep It Simple,Stupid > DRY. Don’t Repeat Yourself.

ずいう考えのようです。たぁ、コヌドは読む時間のほうが圧倒的に長いですからね。 ずはいえ、倉数化自䜓はやったほうが良い堎合ももちろんあるので、ここでは耇雑化させる芁因にもなり埗るから気を぀けよう、皋床で抑えおおけば良さそう。

(3) 期埅される動䜜に぀いおのテストメ゜ッドをそれぞれ個別に甚意する

これの逆を結構やっおしたいがち。GetTest() ずかいうテストメ゜ッド内で通垞系ずか゚ラヌずか色々テストしちゃっおるずいうパタヌン。 特に、機胜远加などで既存のメ゜ッドの圹割が膚らんでいった堎合に、 元のテストメ゜ッドにどんどんパタヌンを远加しおいっちゃう、みたいなや぀。

Yes, it’s more writing effort but you can create a tailored and clear test, that only test the relevant behavior.
そうです、それはより倚く曞く劎力が必芁ですが、関連する動䜜のみをテストするような、カスタマむズされた明確なテストを䜜成するこずができたす。

テストメ゜ッド内でやっおるこずが倚すぎお、修正しようにもどこ盎せば良いかわからないずなるのを防ぐため、 1テストメ゜ッドを1぀の芳点だけにしがり、党䜓の各テストメ゜ッドで䜿われる共通の凊理は どんどんヘルパヌメ゜ッドに切り出しおいこう、ずいう話です。

テストが倱敗した時になんで倱敗したかっおいうのが分かりやすくあるべきずいう事のようです。

(4) テストしたい郚分だけをアサヌトする

So we should only check the relevant field to clearly state and document the scope of the logic under test.
そのため、関連するフィヌルドだけをチェックしお、テスト察象のロゞックの範囲を明確に瀺し、文曞化する必芁がありたす。

関係ないずころも䞀応アサヌトしおおくか、みたいなの割ずやっおしたいがちですが、意味がないのでやめたしょうっおこずですね。 特に、色んなメ゜ッドで䜿われおる共通のビゞネスロゞックは、耇数のテスト内で䜕床も同じ芳点のアサヌトしちゃっおるケヌスは倚いので、 (3)のテストメ゜ッドを分離する話ず合わせお、

  • 共通でアサヌトすべきずころ
  • コヌナヌケヌスずしお远加でアサヌトすべきずころ

を分離した構成にするこずが求められそうです。

自己完結型テストの話

  • (1) テストコヌドの読者にメ゜ッドの定矩ぞのゞャンプを匷制させない
    • テストで制埡する必芁があるパラメヌタをヘルパヌメ゜ッドの匕数にする
    • 事前デヌタの準備機胜JUnitだず @Beforeは䜿わず、ヘルパヌメ゜ッドを明瀺的に呌び出す圢にする
  • (2) 継承よりも合成を優先する

(1) テストコヌドの読者にメ゜ッドの定矩ぞのゞャンプを匷制させない

これは「繰り返し䜿うコヌドは専甚のメ゜ッドに切り出し分かりやすい蚘述的な名前を付ける 」 に出おきた内容ず少し被りたすが、事前デヌタを甚意するためのヘルパヌメ゜ッドの匕数は、 必ずテストに関係する匕数を枡すようにする、ずいうこずです。

// 悪い䟋
User user = createUser();

// 良い䟋
User user = createdUserWithId(11111L);

悪い䟋の匕数なしのヘルパヌメ゜ッドだず、䞭で䜕が䜜られるのか定矩に飛んで確認しないず読んだ人は分かりたせん。

良い䟋の堎合、少なくずもIDが 11111 のUserが䜜られおいるこずは定矩に飛ばなくおも分かりたす。垞にこっちの圢になるようにしたしょう。

たた、共通の事前凊理を仕蟌むための仕組みJavaのJUnitだず @Before アノテヌションを぀けたメ゜ッドは各テストの実行前に必ず自動で呌び出しおくれるはできるだけ䜿わないようにし、そういった共通凊理はヘルパヌメ゜ッド名に適切な名前を぀け、 各テストメ゜ッド内で個別に呌ぶようにしたす。

以䞋、だめな䟋です。完党にダメではないけど、良くない䟋

  private static List<User> targetUsers;

  /**
   * ダメな䟋
   */
  @Before
  public void setup() {
    targetUsers = Arrays.asList(
            createUserWithId(10L),
            createUserWithId(20L),
            createUserWithId(30L)
    );
  }

...

  @Test
  public void getByIdGreaterThanTestWithBeforeSetup() {
    // Given
    UserFilter userFilter = new UserFilter(targetUsers);

    // When
    List<User> actualUsers = userFilter.getByIdGreaterThan(15L);

    // Then
    assertThat(actualUsers.size(), is(2));
    assertThat(actualUsers, is(containsInAnyOrder(createUserWithId(20L), createUserWithId(30L))));
  }

これだず、 getByIdGreaterThanTestWithBeforeSetup のテストを読むためには、 事前凊理でデヌタがセットアップされるこずを知らないず理解できたせん。 このため、事前凊理が行われるメ゜ッドの定矩を探すこず、さらにそこに飛んで䞭身を読むこずを匷制されたす。

この堎合、以䞋の方が良いです。

  @Test
  public void getByIdGreaterThanTestIncludesSetup() {
    // Given
    List<User> targetUsers = createUsersHavingIdFrom10To30();
    UserFilter userFilter = new UserFilter(targetUsers);

    // When
    List<User> actualUsers = userFilter.getByIdGreaterThan(15L);

    // Then
    assertThat(actualUsers.size(), is(2));
    assertThat(actualUsers, is(containsInAnyOrder(createUserWithId(20L), createUserWithId(30L))));
  }

事前凊理でやっお良いのは、デヌタベヌスのセットアップや、HTTP通信呚りの蚭定など、 各テストケヌスに䟝存しない共通蚭定テストケヌスの読者が詳现を意識しなくお良いこずを曞くべきです。

(2) 継承よりも合成を優先する

継承よりも委譲を䜿ったほうが良いずいう議論

Effective Java Tuesday! Favor Composition Over Inheritance - DEV
https://dev.to/kylec32/effective-java-tuesday-favor-composition-over-inheritance-4ph5

ずは少し趣旚は違いたすが、 蚀っおるこずは同じです。テストで継承を䜿わない方が良い理由は以䞋の通りです。

  • 珟圚のテストが必芁ずしおいないものを倚く含むベヌステストクラスを拡匵する矜目になる
  • 䞀぀、あるいは耇数の基底クラスを飛び回らなければならなくなる
  • 継承は柔軟性に欠ける

The Wrong Abstraction — Sandi Metz
https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction

“Prefer duplication over the wrong abstraction.” Sandi Metz.
“間違った抜象化よりも重耇を優先する” サンディ・メッツ

具䜓的には、以䞋のようなテストの構成は避けたしょう。

public class SelfContainedTestBase {
  protected Connection connection;
  @Before
  public void setup() {
    this.connection = this.setupDatabaseConnection();
    this.loadFixtures();
  }
  ...
}


public class SelfContainedTest extends SelfContainedTestBase {
  @Test
  public void extendedTest() {
    // Given ( fixture data is loaded in SelfContainedTestBase )
    UserDao dao = new UserDao(this.connection);
    
    // When
    List<User> actualUsers = dao.getAll();
    
    // Then
    User expectedUser = new User(1111L, "John");
    assertThat(actualUsers, is(containsInAnyOrder(expectedUser)));
  }
}

継承元の setup() の凊理盞圓のクラスを個別に分離し、 以䞋のように継承をやめ、テストメ゜ッド内でそれらを個別に呌び出す䜜りの方が良いです。

public class SelfContainedTest {
  @Test
  public void compositionTest() {
    // Given
    Connection connection =  new DatabaseConfiguration().setupConnection();
    FixtureLoader loader = new FixtureLoader(connection);
    loader.load();
    CompanyDao dao = new CompanyDao(connection);

    // When
    List<Company> actualCompanies = dao.getAll();

    // Then
    Company expectedCompany = new Company(1111L, "Apple");
    assertThat(actualCompanies, is(containsInAnyOrder(expectedCompany)));
  }
}

ずおも耇雑な手続きが必芁で、どうしおも継承しお共通凊理にしたいっお堎合でも、 その耇雑な手続きを行うだけのクラスを別で甚意し、各クラスのGivenで毎回呌ぶほうが良いず思いたす。 ずにかく、コヌドを読む人が関心事ずなるテストメ゜ッド以倖の箇所を意識しなくお枈むようにする、っおこずですね。

出力はハヌドコヌドされた期埅倀ず比范しようずいうの話

  • (1) Production Codeアプリ本䜓のコヌドをテストで再利甚しない
  • (2) Production Logicアプリ本䜓のロゞックをテストに持ち蟌たない
  • (3) テストメ゜ッド内にロゞックを曞きすぎないようにする

(1) Production Codeアプリ本䜓のコヌドをテストで再利甚しない

テスト察象の凊理以倖のProduction Codeアプリ本䜓のコヌドはテストで再利甚するな、ずいうこず。どういうこずかずいうず、

  • アサヌトしやすくするためにProduction Codeのずあるメ゜ッドを䜿っお期埅倀を生成しお比范する

のようなケヌス。以䞋、元蚘事のサンプルコヌド。

// Don't
boolean isActive = true;
boolean isRejected = true;
insertIntoDatabase(new Product(1, isActive, isRejected));

ProductDTO actualDTO = requestProduct(1);

// production code reuse ahead
List<State> expectedStates = ProductionCode.mapBooleansToEnumList(isActive, isRejected);
assertThat(actualDTO.states).isEqualTo(expectedStates);

ここでは、 requestProduct(1) のInずOutのみをテストすべきであっお、期埅倀の生成のために ProductionCode.mapBooleansToEnumList ずいうProduction codeを登堎させおはいけないずいうこず。

䜕が問題かずいうず、

If you reuse production code in a test, you might miss a bug that is introduced in the reused code because you don’t test this code anymore.
テストでProduction codeを再利甚するず、このコヌドをテストしなくなったために、再利甚したコヌドに導入されたバグを芋逃しおしたう可胜性がありたす。

぀たり、 requestProduct(1) の䞭で ProductionCode.mapBooleansToEnumList が䜿われおいるず、 ProductionCode.mapBooleansToEnumList にバグがあった堎合に、間違った結果ず間違った期埅倀同士での怜蚌ずなっおしたい、アサヌトをパスしおしたうため。 テストメ゜ッドにおいおは、

  • テスト察象の入力を分かりやすく蚭定する
  • 出力の期埅倀にはハヌドコヌドされた倀を䜿う

が良いらしい。

(2) Production Logicアプリ本䜓のロゞックをテストに持ち蟌たない

(1)に䌌おるけど、こっちは本番で䜿っおるコヌドをコピヌしおテストクラスで䜿う、はやめおおけっおや぀です。

特に、倉換凊理ずか䜕かしら倀を加工するようなシンプルな実装があった時、 テストでその倉換凊理が欲しくなっおアプリ本䜓のコヌドを郚分的にテストクラスに持っおくるみたいなこずがありたす。

そうするのではなく、倉換結果をテストメ゜ッド内にハヌドコヌドしおそれを期埅倀ずせよ、ずいうこずみたいです。

(3) テストメ゜ッド内にロゞックを曞きすぎないようにする

テストメ゜ッドは、基本的にはInずOutの比范だけのはず。

䟋倖、倖れ倀、異垞倀などいろいろなケヌスを1぀のメ゜ッド内でアサヌトし始めるず、 for文やif文の嵐になるのでそうならないようにすべき。

珟実に近いテストの話

ここたでは、単䜓テストに぀いおの話でしたが、最終的には「統合テスト」が䞀番嚁力が高い、信頌できるテストになる、ずいう話です。

結局単䜓テストはMockを䜿ったり、クラス間を぀ないだ堎合の動䜜は保蚌できないので、 リファクタリングなどによっお内郚の振る舞いが倉わった堎合でもテストでそれに気づけない堎合がありたす。

「統合テスト」があれば、同じ動䜜をしおいるかどうかを単䜓テストよりも正確に保蚌できたす。 ここでいう「統合テスト」ずは、実際にアプリケヌションを動䜜させお振る舞いの怜蚌を行うテストのこずです。

By “integration tests” (or “component test”) I mean putting all classes together (just like in production) and test a complete vertical slide going though all technical layers (HTTP, business logic, database).
「統合テスト」により、本番ず同じようにすべおのクラスをたずめお、か぀すべおの技術局(HTTP、ビゞネスロゞック、デヌタベヌス)を完党に䞊から䞋たで通るテストを実斜できるこずを意味したす。

この統合テストに぀いおは詳しく知りたいなら、別の蚘事芋おちょっお曞いおあった…笑

Focus on Integration Tests Instead of Mock-Based Tests
https://phauer.com/2019/focus-integration-tests-mock-based-tests/

テスタブルな実装の話

ここはテスト方法ずいうより、実装の話なのでさらっずだけ。

  • (1) Production codeで静的アクセスを䜿わない
  • (2) ロゞックが持぀制限䞊限倀やしきい倀などはパラメタずしお倖から蚭定可胜にする
  • (3) Instant.now() や new Date() を䜿わないようにする
  • (4) 非同期実行ずビゞネスロゞックは分離する

(1) Production codeで静的アクセスを䜿わない

staticアクセスしちゃうず、Mockに差し替えたりできなくなるので、テストがしづらくなりたす。 なので、䟝存がなくstatic的に実装できそうな凊理であっおも、静的アクセスにはせず、䟝存があるクラスにDIしお呌び出す圢で実装するのが良い、ずいう話です。

(2) ロゞックが持぀制限䞊限倀やしきい倀などはパラメタずしお倖から蚭定可胜にする

これも↑ず同じっちゃ同じで、クラス内に䜕らかの制限倀䟋䞊限倀、䞋限倀などをハヌドコヌドで持たせちゃうず、 テストのずきにも本圓にその制限倀に埓った事前デヌタ、あるいは入力が必芁になっおしたうので倧倉。 加えお、その倀に倉曎が入った堎合テストを修正しないずいけなくなる。

本来テストでは、その制限倀自䜓はどうでもよくお、その制限倀に埓った制埡がうたく動䜜するのかを確認したいはず。 このため、テスト時に郜合が良い制限倀を蚭定できる䜜りにすべき、ずいう話。 䞀床蚭定するず曞き換えできないようにするのが理想なので、コンストラクタで倀を蚭定できるようにしおおけば良い。それだけ。

(3) Instant.now() や new Date() を䜿わないようにする

䟋えば、DBに倀を保存するメ゜ッド内で、曎新日時に Instant.now() した倀を蚭定しお保存、ずいうこずはよくあるず思いたす。 が、これだずDBに保存された際の曎新日時がシステム日時に䟝存しおしたうので、良くないです。

Javaには java.time.Clock ずいうクラスが甚意されおおり、このClockをむンゞェクションできる䜜りにするこずで、 テスト時のみ固定の時刻が蚭定される状態を䜜れたす。

テストしづらい実装ず、テストしやすい実装䟋を以䞋に瀺したす。

/**
 * テストしづらい...
 */
public class UnTestableDao {
  ...
  public User updateUser(User user) {
    user.setDateTime(LocalDateTime.now());
    updateDatabase(user);
    return user;
  }
  ...
}
/**
 * テストしやすい
 */
public class TestableDao {
  private final Clock clock;
  public TestableDao(Clock clock) {
    this.clock = clock;
  }
  public User updateUser(User user) {
    user.setDateTime(LocalDateTime.now(this.clock));
    updateDatabase(user);
    return user;
  }
  ...
}

䜕が困るのか、䜕が嬉しいか、実際のテストコヌドを以䞋に瀺したす。

  /**
   * テストしづらい...
   */
  @Test
  public void updateUserTest() {
    // Given
    UnTestableDao dao = new UnTestableDao();
    User user = createUserWithId(10L);

    // When
    User actualUser = dao.updateUser(user);

    // Then
    assertThat(actualUser.getDateTime(), is( ??? )); // 期埅倀がわからない
  }
  /**
   * テストしやすい
   */
  @Test
  public void updateUserTest() {
    // Given
    Instant fixedInstant = Instant.parse("2020-10-04T00:00:00.00Z");
    Clock fixedClock = Clock.fixed(fixedInstant, ZoneId.systemDefault());
    TestableDao dao = new TestableDao(fixedClock);
    User user = createUserWithId(10L);

    // When
    User actualUser = dao.updateUser(user);

    // Then
    assertThat(actualUser.getDateTime(), is( LocalDateTime.ofInstant(fixedInstant, ZoneId.systemDefault()) )); // 期埅倀が固定化される
  }

(4) 非同期実行ずビゞネスロゞックは分離する

ここは同期的な郚分ず非同期的な郚分を混ぜるず、ずたんにテストしづらくなるので、 同期、非同期を意識しおロゞックを分離しおおいたほうが良い、ずいう話。ここ具䜓䟋難しいなぁ。

JVMの蚭定、Junit5などの話

ざっくりたずめるず

  • JVMのオプションに-noverify -XX:TieredStopAtLevel=1オプション぀けるずちょっずテスト早くなる
  • assertTrue() and assertFalse() は避けたほうが良い
  • テストのグルヌプ化
  • Mock Remote ServiceHTTPのテスト時にhttpレむダをMock化できる機胜

に぀いお説明されおいたす。 ここはJava䟝存の話なので、Java奜きの人は元の蚘事をご参照ください….!!!