web-dev-qa-db-ja.com

JUnitテストでのGuiceインジェクター

Guiceを使用して、各テストクラスは独立している必要があるため、各JUnitテストクラスで新しいインジェクターを取得することは良い方法ですか?

19
Alexis Dufrenoy

Guice Berry を見てください。

今は使用しないことをお勧めします(ドキュメントは本当にひどいです)が、それらのアプローチを見ると、jUnitでDIをどのように実行する必要があるかを明確に理解できます。

5
Sotomajor

各テストは手動のDIを管理できるほど十分に小さくなければならないため、単体テストでGuiceを使用することは本当に避けてください。ユニットテストでGuice(または任意のDI)を使用することで、クラスが大きくなりすぎて責任が多すぎるという警告を隠しています。

ブートストラップコードと統合テストをテストするには、テストごとに異なるインジェクターを作成します。

34

誰かがこの質問に出くわしてユニットテストから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
    }
}
26
Joe Abrams

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);
    }

}
10
Adi Sembiring

これは、使用している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;    

    }
4
Chris S.

私は AtUnit がGuiceの優れた補完物であることがわかりました(モックフレームワークの統合も扱っています)。

これにより、単体テストクラスが非常に明確かつ簡潔になり(Injectorは表示されません)、必要に応じて、単体テストの一部としてプロダクションバインディングを実行することもできます。

2
sxc731

私が最近書いたこのフレームワーク Guice-Behave をお勧めします。

非常にシンプルで、2つのアノテーションを使用して、アプリケーションの同じコンテキストでテストを実行できます。

Guiceモジュール内でモックを定義できます。これにより、モックを簡単に再利用できます。

2
Alessandro