Guiceを使用して、各テストクラスは独立している必要があるため、各JUnitテストクラスで新しいインジェクターを取得することは良い方法ですか?
Guice Berry を見てください。
今は使用しないことをお勧めします(ドキュメントは本当にひどいです)が、それらのアプローチを見ると、jUnitでDIをどのように実行する必要があるかを明確に理解できます。
各テストは手動のDIを管理できるほど十分に小さくなければならないため、単体テストでGuiceを使用することは本当に避けてください。ユニットテストでGuice(または任意のDI)を使用することで、クラスが大きくなりすぎて責任が多すぎるという警告を隠しています。
ブートストラップコードと統合テストをテストするには、テストごとに異なるインジェクターを作成します。
誰かがこの質問に出くわしてユニットテストからGuiceアノテーションを機能させる方法を確認したい場合は、以下のような基本クラスからテストを拡張し、injector.injectMembers(this);
を呼び出します。
public class TestBase {
protected Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(HelloService.class);
}
});
@Before
public void setup () {
injector.injectMembers(this);
}
}
それからあなたのテストはこのように注入されたHelloService
を得ることができます
public class HelloServiceTest extends TestBase {
@Inject
HelloService service;
@Test
public void testService() throws Exception {
//Do testing here
}
}
DI
を使用すると単体テストコードがより簡単になると思います。単体テストと統合テストには常にDIを使用します。
DIがないと、すべてをコーディングするのが難しくなります。 Guice Inject or Spring Autowired
を使用します。以下の私のテストコードのように:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/application-context.xml")
public class When_inexists_user_disabled {
@Autowired
IRegistrationService registrationService;
private int userId;
@Before
public void setUp() {
Logger.getRootLogger().setLevel(Level.INFO);
Logger.getLogger("org.springframework").setLevel(Level.WARN);
BasicConfigurator.configure();
userId = 999;
}
@Test(expected=UserNotFoundException.class)
public void user_should_have_disabled() throws UserNotFoundException {
registrationService.disable(userId);
}
}
これは、使用しているJUnitのバージョンによって異なります。私たちのチームはJunit4をうまく使用しており、現在JUnit5を調査しています。
Junit5では、拡張機能を使用します。
public class InjectionPoint implements BeforeTestExecutionCallback {
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
List<Module> modules = Lists.newArrayList(new ConfigurationModule());
Optional<Object> test = context.getTestInstance();
if (test.isPresent()) {
RequiresInjection requiresInjection = test.get().getClass().getAnnotation(RequiresInjection.class);
if (requiresInjection != null) {
for (Class c : requiresInjection.values()) {
modules.add((Module) c.newInstance());
}
}
Module aggregate = Modules.combine(modules);
Injector injector = Guice.createInjector(aggregate);
injector.injectMembers(test.get());
getStore(context).put(injector.getClass(), injector);
}
}
private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass()));
}
}
次に、各テストは、Insumeモジュールの配列を受け入れて集約するか、なしを使用してデフォルトを使用できるRequiresInjectionアノテーションを使用します。
@RequiresInjection
public class Junit5InjectWithoutModuleTest {
@Inject
private TestEnvironment environment;
@Test
public void shouldAccessFromOuterModule() {
assertThat(environment).isNotNull();
}
}
そしてここに注釈があります:
@ExtendWith(InjectionPoint.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
public @interface RequiresInjection {
Class<? extends Module>[] values() default {};
}
JUnit5はまだ新しいので、テンプレートを調べているかもしれませんが、これまでのところ、拡張機能でうまくいくようです。
JUnit4では、カスタムテストランナーのcreateTestメソッド内でインジェクションが行われ、各テストが「getModule」メソッドを持つRequiresInjectionインターフェースを実装することを除いて、同様のアプローチを使用します。
Guiceのサポートが組み込まれているので、TestNGにも一言コメントする必要があります。使用方法は次のように簡単です。
@Guice({SomeObjectModule.class})
public class MyTest {
@Inject
SomeObject someObject;
}
私は AtUnit がGuiceの優れた補完物であることがわかりました(モックフレームワークの統合も扱っています)。
これにより、単体テストクラスが非常に明確かつ簡潔になり(Injector
は表示されません)、必要に応じて、単体テストの一部としてプロダクションバインディングを実行することもできます。
私が最近書いたこのフレームワーク Guice-Behave をお勧めします。
非常にシンプルで、2つのアノテーションを使用して、アプリケーションの同じコンテキストでテストを実行できます。
Guiceモジュール内でモックを定義できます。これにより、モックを簡単に再利用できます。