東京理科大学 Advent Calendar 2019の9日目です。
今回はJavaのフレームワークSpringにおいて @Value
アノテーションを用いたクラスに対してテストをする際の方法/コツについて書いていきます。
@Value
とは
@Value
アノテーションは別のファイルから値をインジェクションしたいときに用いるアノテーションです。
Spring Bootの場合デフォルトの設定のままですと、application.ymlまたはapplication.propertiesに記載してある値から読み取りインジェクションします。
具体的に書くとこのような形式です。
まず、application.ymlに値を書きます。
sample.threshold : 1
そして、アノテーションの引数に
@Value("${先程のkey}")
としたうえで、クラス内のフィールドや引数などに@Value
アノテーションを付与します。
具体的には
@Service
public class HogeCoordinator {
@value("${sample.threshold}")
private Integer threshold;
@Autowired
private HogeService hogeService;
public void hoge() {
hogeService.fuga(threshold);
}
}
このようなイメージです。
HogeCoordinatorクラスのフィールドthresholdに@value("${sample.threshold}")
と記載するとapplication.ymlに記載したのkeyから1を読み取り、アプリケーションを実行するとインジェクションしてくれます。
これにより、アプリケーション固有の値などを1ファイルに集めて管理できるのがメリットです。
@Value
を用いているクラスに対するテスト
それでは本題の@Value
を用いているクラスに対するテスト方法です。
テスト対象は先程のHogeCoordinator.javaとします。
@Value
を用いている場合、下記のような一般的なテストの書き方をしてしまうと、@Value
を付与している対象に値をインジェクションしてくれません。
public class FailedHogeCoordinatorTest {
@InjectMocks
private HogeCoordinator hogeCoordinator;
@Mock
private HogeService hogeService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
// 以下省略
}
そこで、ReflectionTestUtilsクラスのsetFieldメソッドを用います。メソッドの引数には対象のインスタンス、インジェクションを行うフィールド名、インジェクションする値を指定します。
このメソッドを用いるために、HogeCoordinatorのインスタンスを 生成しておく必要があるため、newしておきます。
これを踏まえてテストを書くとこのようになります。
public class SuccessHogeCoordinatorTest {
@InjectMocks
private HogeCoordinator hogeCoordinator = new HogeCoordinator();
@Mock
private HogeService hogeService;
@Before
public void setup() {
ReflectionTestUtils.setField(hogeCoordinator, "threshold", 1);
MockitoAnnotations.initMocks(this);
}
// 以下省略
}
こうすることで、@Value
を付与しているフィールドに値がインジェクションされます。
コツとしてはモックをインジェクションするクラスのインスタンスをテスト時に生成する必要があるため、DIを行う際にはコンストラクタインジェクションをしない方がよいということです。
今回のテスト対象であるHogeCoordinatorクラスは@Autowired
を用いてDIしていますが、例えば下記のようにLombokの@RequiredArgsConstructor
を用いてコンストラクタインジェクションをしてしまうと、コンストラクタの引数が増えてしまうため、インスタンスの生成が面倒になります。
@Service
@RequiredArgsConstructor
public class HogeCoordinator {
@value("${sample.threshold}")
private Integer threshold;
private final HogeService hogeService;
public void hoge() {
hogeService.fuga(threshold);
}
}
そのため、ReflectionTestUtilsクラスのsetFieldメソッドを用いてテストをするクラスにインジェクションする場合は、コンストラクタインジェクションは避けた方が楽になると考えます。
まとめ
@Value
を付与しているクラスに対してテストする際はReflectionTestUtilsクラスのsetFieldメソッドを用いると値をインジェクションしてくれます。
その際にテスト対象のクラスのDIはコンストラクタインジェクション以外の方法にしておいた方が、テストの際にインスタンスの生成が容易になり、楽にテストすることができます。
さいごに
今回OBという縁がありまして、アドベントカレンダーに参加させていただくことにしました。
私自身初のアドベントカレンダーへの参加ということもあり、貴重な場を設けてくださった@piffettさんには感謝しております。
ありがとうございました。