JUnit 测试中的 Guice 注入器

发布于 2024-10-31 21:51:22 字数 1431 浏览 1 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

熊抱啵儿 2024-11-07 21:51:22

如果有人偶然发现这个问题并希望了解如何从单元测试中获取 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
    }
}

In case anyone stumbles upon this question and wants to see how to get Guice annotations working from unit tests, extend your tests from a base class like the one below and call 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);
    }
}

Then your test can get an injected HelloService like this

public class HelloServiceTest extends TestBase {
    @Inject
    HelloService service;

    @Test
    public void testService() throws Exception {
       //Do testing here
    }
}
七颜 2024-11-07 21:51:22

您确实应该避免在单元测试中使用 Guice,因为每个测试都应该足够小,以便手动 DI 易于管理。通过在单元测试中使用 Guice(或任何 DI),您可以隐藏一个警告,即您的类变得太大并且承担了太多的责任。

为了测试引导程序代码和集成测试,可以为每个测试创建一个不同的注入器。

You should really avoid using Guice in unit tests as each test should be small enough that manual DI is manageable. By using Guice (or any DI) in unit tests you are hiding away a warning that your class is getting too big and taking on too many responsibilities.

For testing the bootstrapper code and integration tests then yes create a different injector for each test.

陪你到最终 2024-11-07 21:51:22

我认为使用 DI 会让单元测试代码更加简单,我总是使用 DI 进行单元测试以及集成测试。

如果没有 DI,一切都很难编码。使用 Guice Inject 或 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);
    }

}

I think using DI will make unit test code more simple, I always Use DI for unit test and also for integration test.

Without DI everything feels hard to code. Either using Guice Inject or Spring Autowired. like my test code bellow:

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

}
白昼 2024-11-07 21:51:22

这取决于所使用的 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()));
        }

    }

然后每个测试都使用 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 接口。

我可能也应该对 TestNG 表示感谢,因为 Guice 支持是内置的。用法就像这样简单:

@Guice({SomeObjectModule.class})
    public class MyTest {

        @Inject
        SomeObject someObject;    

    }

This depends on which version of JUnit is being used. Our teams have used Junit4 successfully and are now looking into JUnit5.

In Junit5 we use extensions.

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

    }

Then each test uses the RequiresInjection annotation, which can accept an array of inner modules to aggregate, or none to use the default.

    @RequiresInjection
    public class Junit5InjectWithoutModuleTest {

        @Inject
        private TestEnvironment environment;

        @Test
        public void shouldAccessFromOuterModule() {
            assertThat(environment).isNotNull();
        }

    }

And here's the annotation:

    @ExtendWith(InjectionPoint.class)
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
    public @interface RequiresInjection {

        Class<? extends Module>[] values() default {};

    }

JUnit5 is still new to me, so I may be looking into templates, but so far the Extensions seem to do the trick.

With JUnit4 we use a similar approach, except that the injection takes place within the createTest method of our custom test runner, and then each test implements a RequiresInjection interface that has a "getModule" method.

I should probably give a shout out to TestNG as well, as Guice support is built right in. Usage is as simple as this:

@Guice({SomeObjectModule.class})
    public class MyTest {

        @Inject
        SomeObject someObject;    

    }
酒与心事 2024-11-07 21:51:22

看看 Guice Berry

我现在不建议使用它(文档真的很糟糕),但是看看他们的方法可以让你清楚地思考在 jUnit 中应该如何完成 DI。

Take a look at Guice Berry.

I won't recommend using it now (documentation is really terrible), but looking at their approach can make you think clear about how DI should be done in jUnit.

〆一缕阳光ご 2024-11-07 21:51:22

我发现 AtUnit 是 Guice 的绝佳补充(它甚至可以处理模拟框架集成)。

这使得单元测试类极其清晰和简洁(永远不会在那里看到注入器),并且在适当的情况下,还允许您将生产绑定作为单元测试的一部分进行练习。

I found AtUnit to be an excellent complement to Guice (it even deals with mock framework integration).

This makes the Unit Test classes extremely clear and concise (never see an Injector there) and, where appropriate, also lets you exercise your production bindings as part of your unit tests.

贩梦商人 2024-11-07 21:51:22

我建议使用我最近编写的这个框架 Guice-Behave

它非常简单,通过两个注释,您可以在应用程序的同一上下文中运行测试。

您可以在 Guice 模块中定义您的模拟,这样就可以很容易地重用它们。

I suggest this framework I have recently written Guice-Behave.

It is very simple, with two annotations you can run the test in the same context of your application.

You can define your mocks inside the Guice module and in this way it is very easy to re-use them.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文