如何从 JerseyTest 子类访问 Spring Bean

How to access Spring Bean from JerseyTest subclass

这是我的摘要 class,它以给定的 Spring 上下文开始 Jersey:

public abstract class AbstractJerseyTest extends JerseyTest {

public void setUp() throws Exception {
    super.setUp();
}

@AfterClass
public void destroy() throws Exception {
    tearDown();
}

@Override
protected URI getBaseUri() {
    return URI.create("http://localhost:9993");
}

@Override
protected Application configure() {
    RestApplication application = new RestApplication();

    Map<String, Object> properties = new HashMap<String, Object>();
    properties.put(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
    properties.put("contextConfigLocation", "classpath:spring-context-test.xml");

    application.setProperties(properties);
    application.register(this);
    return application;
}
}

所以,问题是我需要从我的测试中访问 Spring bean 以用一些数据填充数据库。

球衣版本为 2.6

我也发现了一个类似的问题here

但它与 Jersey 1.x 有关,因此它不适用于 Jersey 2.x

谁能指出我正确的方向?

通常在你的情况下,我只是说使用模拟工作,但在某些情况下你可能需要在测试中公开服务 class。

要在没有任何“丑陋的技巧”的情况下做到这一点,您需要掌握 ServiceLocator(类似于 Spring 的 ApplicationContext)。当 Jersey 应用程序启动时,来自 ApplicationContext 的所有 Spring 服务都通过 HK2 的 Spring 桥放入 ServiceLocator

问题是 JerseyTest 没有以任何方式公开 ServiceLocator。我能想到的唯一方法是创建你自己的 TestContainerFactory,并创建 ApplicationHandler,它公开了 ServiceLocator.

尝试实现自己的 TestContainerFactory 不是在公园散步,如果您不知道自己在做什么。最简单的做法就是查看 Jersey InMemoryTestContainerFactory 的源代码。如果您查看内部 class InMemoryTestContainer 的构造函数,您会看到它创建了 ApplicationHandler。这就是通过 appHandler.getServiceLocator().

公开 ServiceLocator 的方法

因此,如果您复制 class,并公开 ServiceLocator,您可以创建 JerseyTest 扩展,并调用 ServiceLocator.inject(Object) 方法来注入测试 class.

public abstract class AbstractServiceLocatorAwareJerseyTest extends JerseyTest {
    
    private final ServiceLocatorAwareInMemoryTestContainerFactory factory
            = new ServiceLocatorAwareInMemoryTestContainerFactory();
    private ServiceLocator locator;
    
    @Override
    public TestContainerFactory getTestContainerFactory() {
        return factory;
    }
    
    @Before
    @Override
    public void setUp() throws Exception {
        super.setUp();
        this.locator = factory.getServiceLocator();
        if (injectTestClass()) {
            this.locator.inject(this);
        }
    }
    
    public boolean injectTestClass() {
        return true;
    }
    
    public ServiceLocator getServiceLocator() {
        return locator;
    }
}

如果出于任何原因您需要它,ServiceLocator 也有 ApplicationContext,如果需要,您也可以将其公开给您的测试 class。

我整理了一份 GitHub project,其中包含完整的实现和测试,如果你想看一下的话。


更新

尽管 OP 对这个问题的回答有效,但我认为它有效的事实是一个错误。我最初删除了这个答案,在 OP post 编辑了他们的答案之后,但经过一些测试,我认为这个解决方案是一个错误,所以我为任何不喜欢的人取消删除这个 post warning1 你在使用那个解决方案时得到


1. “警告:在 SERVER 运行时中注册的提供者 SimpleTest 未实现适用于 SERVER 运行时的任何提供者接口。由于约束配置问题,提供者 SimpleTest 将被忽略。”

解决方法非常简单。 我补充说:

@Autowired
private Repository repository;

到 AbstractJerseyTest 并且该字段在测试启动期间自动连接。我不知道它是如何工作的细节,但似乎当我在 REST 应用程序中注册测试实例时

application.register(this);

它会自动装配测试中的所有 bean。