フィールドインジェクションとコンストラクタインジェクションの違い(SpringフレームワークDI)
フィールドインジェクションとコンストラクタインジェクションの違い
フィールドインジェクションとコンストラクタインジェクションは、Springフレームワークにおける依存性注入(DI)の方法です。ここでは、それぞれの違いやデメリット、そしてテストコードの具体例について説明します。
フィールドインジェクション
フィールドインジェクションは、@Autowired
アノテーションをフィールドに直接付けて、Springがそのフィールドに依存オブジェクトを注入する方法です。以下は、フィールドインジェクションの具体的な実装例です。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MyController {
@Autowired
private ItemService itemService;
@GetMapping("/items")
public String getItems(Model model) {
model.addAttribute("items", itemService.getAllItems());
return "itemList";
}
}
@Service
public class ItemService {
public List getAllItems() {
return List.of("Item1", "Item2", "Item3");
}
}
- メリット: シンプルでコードが簡潔です。追加のコンストラクタやセッターを記述する必要がなく、クラスが短く保たれます。
- デメリット:
- 依存関係の非表示性: クラスの依存関係が明示されないため、コードの可読性が低下しやすく、依存関係が把握しづらい。
- 不変性の欠如: フィールドを
final
にできず、一度設定された依存関係が変更される可能性があります。これは設計上の安全性を低下させます。 - Springコンテナへの強い依存: クラスがSpringコンテナ内でしか動作しなくなり、単独での使用やテストが困難になる可能性があります。
- 隠れた複雑性: 大規模プロジェクトや複雑なクラス設計では、フィールドインジェクションが設計を理解しにくくし、メンテナンスを難しくすることがあります。
コンストラクタインジェクション
コンストラクタインジェクションは、依存オブジェクトをクラスのコンストラクタを通じて注入する方法です。これにより、依存関係がクラスの外部から明示的に注入され、フィールドをfinal
にすることができ、コードの安全性が向上します。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MyController {
private final ItemService itemService;
@Autowired
public MyController(ItemService itemService) {
this.itemService = itemService;
}
@GetMapping("/items")
public String getItems(Model model) {
model.addAttribute("items", itemService.getAllItems());
return "itemList";
}
}
@Service
public class ItemService {
public List getAllItems() {
return List.of("Item1", "Item2", "Item3");
}
}
- メリット:
- 依存関係が明示され、クラスの設計が明確になります。
- フィールドを
final
にでき、一度設定された依存オブジェクトが変更されないことが保証されます。 - Springコンテナ以外でも、簡単に依存関係を提供できるため、テストやモジュール化が容易になります。
- デメリット:
- コードの冗長性: 依存関係が多い場合、コンストラクタが冗長になることがあり、コードが長くなります。
- 大規模コンストラクタの複雑さ: コンストラクタに多くの依存オブジェクトが必要な場合、コードの可読性が低下することがあります。
テストコードにおける when-thenReturn
と doReturn-when
の違い
テストコードを書く際に、when-thenReturn
とdoReturn-when
は、Mockitoフレームワークでスタブを設定するための方法です。それぞれに適した場面があり、以下にその違いを説明します。
when-thenReturn
when-thenReturn
は、通常のスタブを設定するために使用されます。モックされたオブジェクトの特定のメソッドが呼び出されたときに、指定した値を返すように設定します。通常、このメソッドはリターンタイプを持ち、呼び出し時に特定の戻り値を返すように定義されます。
when(mockService.someMethod()).thenReturn("expectedValue");
この場合、mockService.someMethod()
が呼び出されると、常に"expectedValue"が返されます。
doReturn-when
doReturn-when
は、when-thenReturn
が適用できない特殊なケースで使用されます。例えば、ターゲットメソッドがfinal
であったり、例外が発生する場合、またはメソッド呼び出しによる副作用を避けたい場合に適しています。doReturn-when
を使用することで、メソッド呼び出しの副作用を発生させることなく、戻り値を設定することができます。
doReturn("expectedValue").when(mockService).someMethod();
この場合も、mockService.someMethod()
が呼び出されると"expectedValue"が返されますが、when-thenReturn
で発生する可能性のある副作用が回避されます。
各インジェクション時のテストコードの具体例
フィールドインジェクションの場合
@RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
@InjectMocks
private MyController myController;
@Mock
private ItemService itemService;
@Test
public void testGetItems_whenThenReturn() {
when(itemService.getAllItems()).thenReturn(List.of("Item1", "Item2"));
String view = myController.getItems(new ExtendedModelMap());
assertEquals("itemList", view);
verify(itemService).getAllItems();
}
@Test
public void testGetItems_doReturnWhen() {
doReturn(List.of("Item1", "Item2")).when(itemService).getAllItems();
String view = myController.getItems(new ExtendedModelMap());
assertEquals("itemList", view);
verify(itemService).getAllItems();
}
}
コンストラクタインジェクションの場合
public class MyControllerTest {
@Test
public void testGetItems_whenThenReturn() {
ItemService mockService = mock(ItemService.class);
when(mockService.getAllItems()).thenReturn(List.of("Item1", "Item2"));
MyController myController = new MyController(mockService);
String view = myController.getItems(new ExtendedModelMap());
assertEquals("itemList", view);
verify(mockService).getAllItems();
}
@Test
public void testGetItems_doReturnWhen() {
ItemService mockService = mock(ItemService.class);
doReturn(List.of("Item1", "Item2")).when(mockService).getAllItems();
MyController myController = new MyController(mockService);
String view = myController.getItems(new ExtendedModelMap());
assertEquals("itemList", view);
verify(mockService).getAllItems();
}
}
when-thenReturn と doReturn-when の使い分け
when-thenReturn: 通常のスタブを設定する際に使います。この方法は、対象メソッドが呼び出されたときに指定した値を返すようにします。対象メソッドが実際に呼び出されることを前提にしています。
doReturn-when: 例外やメソッドがfinal
であるなど、特定の状況でwhen-thenReturn
が適用できない場合に使用します。また、メソッドの呼び出しによる副作用を避けたい場合にも利用されます。doReturn-when
は、スタブ設定の前にメソッド呼び出しを避ける柔軟性を提供します。
コメント
0 件のコメント :
コメントを投稿