Resource model validation failure in JerseyTest fo

2020-04-13 05:00发布

I have an annotated Spring-Jersey application. I am trying to set up unit test for my controllers using JerseyTest. I am getting the following errors on running the test that I am not able to figure out. What have I missed?

SEVERE: Following issues have been detected: 
WARNING: No injection source found for a parameter of type public com.example.services.dto.UnitDto com.example.apis.UnitResource.getUnits(com.example.services.dto.PointDto) throws com.example.exceptions.PointOutsideBounds at index 2.

Validation of the application resource model has failed during application initialization.
[[FATAL] No injection source found for a parameter of type public com.example.services.dto.UnitDto com.example.apis.UnitResource.getUnits(com.example.services.dto.PointDto) throws com.example.exceptions.PointOutsideBounds at index 2.; source='ResourceMethod{httpMethod=GET, consumedTypes=[], producedTypes=[application/json], suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class com.example.apis.UnitResource, handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor@31a5870e]}, definitionMethod=public com.example.services.dto.UnitDto com.example.apis.UnitResource.getUnits(com.example.services.dto.PointDto) throws com.example.exceptions.PointOutsideBounds, parameters=[Parameter [type=class com.example.services.dto.PointDto, source=contains, defaultValue=null]], responseType=class com.example.services.dto.UnitDto}, nameBindings=[]}']
    at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:555)

The controller class is as follows:

@Component
@Path("/app/units")
public class UnitResource {
    @Context
    private UriInfo uriInfo;
    @Context
    private Request request;

    @Inject
    private UnitService unitService;

    @Inject
    public UnitResource(@Named("UnitService") UnitService unitService) {
        this.unitService = unitService;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Timed(absolute = true, name = "getUnitForPoint")
    public UnitDto getUnits(@QueryParam("contains") @NotNull PointDto point) throws PointOutsideBounds {
        return unitService.getUnitForPoint(point);
    }
}

The test class is as follows:

public class UnitResourceTest extends JerseyTest {

    @Mock
    private UnitService unitService;

    @Override
    protected ResourceConfig configure() {
        ResourceConfig rc = new ResourceConfig()
        //      .register(new UnitResource(Mockito.mock(UnitService.class))) -- has the same effect
                .register(UnitResource.class)
                .property("contextConfig", new AnnotationConfigApplicationContext(ApplicationConfiguration.class));

        enable(TestProperties.LOG_TRAFFIC);
        enable(TestProperties.DUMP_ENTITY);
        forceSet(TestProperties.CONTAINER_PORT, "0");

        return rc;
    }

    @Before
    public void setUp() throws Exception {
        super.setUp();
        initMocks(this);
    }

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

    @Test
    public void testGetUnit() throws PointOutsideBounds {

        Response response = target()
                .path("/app/units")
                .queryParam("contains", new Point(0.0,0.0).toJson())
                .request(MediaType.APPLICATION_JSON_TYPE)
                .get();

        assertThat(response.getStatus(), is(Response.Status.OK));
    }
}

The application configuration class is:

@Configuration
@ComponentScan("com.example")
public class ApplicationConfiguration {
    @Bean(name="UnitService")
    public UnitService unitService() {
        return new UnitService();
    }
}

My Gradle dependencies for test configuration are:

dependencies {
    // Using junit-dep package to get junit without hamcrest dependency
    testCompile("junit:junit-dep:4.8.2")
    testCompile("org.hamcrest:hamcrest-all:1.3")
    testCompile("org.mockito:mockito-all:1.8.4")
    testCompile ('org.glassfish.jersey.test-framework:jersey-test-framework-core:2.22.2')
    testCompile('org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-inmemory:2.22.2')
}

configurations {
    testCompile.exclude group: 'org.glassfish.jersey.ext', module: 'jersey-spring3'
}

I found this and this but neither solved my problem.

1条回答
贼婆χ
2楼-- · 2020-04-13 05:57

Note: For the most part, this answer applies to all @XxxParam annotated parameters.

The PointDto parameter seems to be the problem

public UnitDto getUnits(@QueryParam("contains") @NotNull PointDto point)

Query parameters are Strings. Jersey has the capability to convert that String to some basic types like int, boolean, Date, etc. But it has no idea how to create the PointDto from that String. That doesn't mean that we can't use custom types. We just need to follow some rules:

  1. Have a constructor that accepts a String. Then we need to construct the object ourselves from that String. For example

    public PointDto(String param) {
        Map<String, Object> map = parseJson(param);
        this.field1 = map.get("field1");
        this.field2 = map.get("field2");
    }
    

    It's a bit of work.

  2. Another option is to have a static fromString(String) or static valueOf(String) in the class. Jersey will invoke this method to construct the object. So for instance you can have in your PointDto class

    public static PointDto fromString(String param) {
        PointDto dto = parseJson(param);
        return dto;
    }
    
  3. You can have a ParamConverterProvider. I won't put an example, but you can read more in this good article about how you can implement it. You can also see this post

Note, the information presented in this answer is in the javadoc for @QueryParam.

查看更多
登录 后发表回答