测试类中的Jersey Test @Autowired字段为null

时间:2013-10-15 15:43:37

标签: java spring junit jersey jersey-client

我有点问题。我认为这是一个典型的问题。但是,我找不到好的榜样。我的申请是使用泽西岛。我想通过客户端测试控制器作为测试。 Controller有私有字段 - StudentService。当我调试测试时,我看到,该字段为空。这会导致错误。我需要注入这个字段。我试过这个: 我的控制器

@Path("/student")
@Component
public class StudentResourse {
    @Autowired
    private StrudentService service; // this field Spring does not set

    @Path("/getStudent/{id}")
    @GET
    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    public Student getStudent(@PathParam("id") long id) {
         return service.get(id);
    }  
}

我的JUnit测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:config.xml")
@TestExecutionListeners({ DbUnitTestExecutionListener.class,
    DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    TransactionalTestExecutionListener.class })
public class StudentResourseTest extends JerseyTest {
private static final String PACKAGE_NAME = "com.example.servlet";
private static final String FILE_DATASET = "/data.xml";
@Autowired
private StudentService service; // this field is setted by Spring, but I do not need this field for test

public StudentResourseTest() {
    super(new WebAppDescriptor.Builder(PACKAGE_NAME).build());
}

@Override
protected TestContainerFactory getTestContainerFactory() {
    return new HTTPContainerFactory();
}

@Override
protected AppDescriptor configure() {
    return new WebAppDescriptor.Builder("restful.server.resource")
            .contextParam("contextConfigLocation",
                    "classpath:/config.xml").contextPath("/")
            .servletClass(SpringServlet.class)
            .contextListenerClass(ContextLoaderListener.class)
            .requestListenerClass(RequestContextListener.class).build();
}

@Test
@DatabaseSetup(FILE_DATASET)
public void test() throws UnsupportedEncodingException {
        ClientResponse response = resource().path("student").path("getStudent")
                .path("100500").accept(MediaType.APPLICATION_XML)
                .get(ClientResponse.class);
        Student student = (Student) response.getEntity(Student.class);
}  }

我猜,这个问题出现在测试类中。因为,当我运行我的应用程序而不是测试时,我可以直接请求学生和一切正常工作。但是当我测试类时,Controller的内部字段没有设置。如何解决这个错误?谢谢你的回答。

这是我的config.xml

<context:component-scan base-package="com.example" />
<bean id="StudentResourse" class="com.example.servlet.StudentResourse">
    <property name="service" ref="studentService" />
</bean>
<bean id="service" class="com.example.service.StudentServiceImpl" />

2 个答案:

答案 0 :(得分:1)

一个问题可能是您尝试在构造函数和configure()方法中配置测试应用程序。使用一个或另一个但不能同时使用两者,因为在这种情况下,您的configure()方法不会被调用,因此您可能没有使用SpringServlet以及此方法中定义的所有内容。

答案 1 :(得分:0)

参考:https://github.com/jiunjiunma/spring-jersey-testhttp://geek.riffpie.com/unit-testing-restful-jersey-services-glued-together-with-spring/

想法是通过使用ApplicationContextAware接口来获取运动衫内的应用程序上下文。之后,我们可以抓取spring之前已经创建的确切bean,在您的情况下为StudentService。下面的示例显示了依赖项SampleService的模拟版本,用于测试资源层api。

将处理委托给服务层的资源类

@Component
@Path("/sample")
public class SampleResource {

    @Autowired
    private SampleService sampleService;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path ("/{id}")
    public Sample getSample(@PathParam("id") int id) {
        Sample sample = sampleService.getSample(id);
        if (sample == null) {
            throw new WebApplicationException(Response.Status.NOT_FOUND);
        }
        return sample;
    }
}

封装业务逻辑的服务层

@Service
public class SampleService {
    private static final Map<Integer, Sample> samples = new HashMap<>();

    static {
        samples.put(1, new Sample(1, "sample1"));
        samples.put(2, new Sample(2, "sample2"));
    }

    public Sample getSample(int id) {
        return samples.get(id);
    }
}

上述资源的单元测试

public class SampleResourceTest extends SpringContextAwareJerseyTest {
    private SampleService mockSampleService;

    // create mock object for our test
    @Bean
    static public SampleService sampleService() {
        return Mockito.mock(SampleService.class);
    }

    /**
     * Create our own resource here so only the test resource is loaded. If
     * we use @ComponentScan, the whole package will be scanned and more
     * resources may be loaded (which is usually NOT what we want in a test).
     */
    @Bean
    static public SampleResource sampleResource() {
        return new SampleResource();
    }

    // get the mock objects from the internal servlet context, because
    // the app context may get recreated for each test so we have to set
    // it before each run
    @Before
    public void setupMocks() {
         mockSampleService = getContext().getBean(SampleService.class);
    }

    @Test
    public void testMock() {
        Assert.assertNotNull(mockSampleService);
    }

    @Test
    public void testGetSample() {
        // see how the mock object hijack the sample service, now id 3 is valid
        Sample sample3 = new Sample(3, "sample3");
        Mockito.when(mockSampleService.getSample(3)).thenReturn(sample3);

        expect().statusCode(200).get(SERVLET_PATH + "/sample/3");
        String jsonStr = get(SERVLET_PATH + "/sample/3").asString();
        Assert.assertNotNull(jsonStr);
    }

}

SpringContextAwareJerseyTest

@Configuration
public class SpringContextAwareJerseyTest extends JerseyTest {
    protected static String SERVLET_PATH = "/api";

    final private static ThreadLocal<ApplicationContext> context =
        new ThreadLocal<>();

    protected String getResourceLocation() {
        return "example.rest";
    }

    protected String getContextConfigLocation() {
        return getClass().getName();
    }

    static private String getContextHolderConfigLocation() {
        return SpringContextAwareJerseyTest.class.getName();
    }

    protected WebAppDescriptor configure() {
        String contextConfigLocation = getContextConfigLocation() + " " +
            getContextHolderConfigLocation();

        Map<String, String> initParams = new HashMap<>();
        initParams.put("com.sun.jersey.config.property.packages",
                       getResourceLocation());
        initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "true");

        return new WebAppDescriptor.Builder(initParams)
            .servletClass(SpringServlet.class)
            .contextParam(
                "contextClass",
                "org.springframework.web.context.support.AnnotationConfigWebApplicationContext")
            .contextParam("contextConfigLocation", contextConfigLocation)
            .servletPath(SERVLET_PATH)  // if not specified, it set to root resource
            .contextListenerClass(ContextLoaderListener.class)
            .requestListenerClass(RequestContextListener.class)
            .build();
    }

    protected final ApplicationContext getContext() {
        return context.get();
    }

    @Bean
    public static ContextHolder contextHolder() {
        return new ContextHolder();
    }

    private static class ContextHolder implements ApplicationContextAware {
        @Override
        public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
            context.set(applicationContext);
        }
    }
}

在jersey 1.8中使用以上内容