0

Completely new to spring-boot.

I am trying to write a test for my POST method that returns a response of CREATED with the path of the resource. I am coming across all sorts of problems for something that I was assuming is very straight forward. In the code bellow is my current test class which seems to be crashing with a

ClassNotFoundException

on the first line of my method.

@RunWith(SpringRunner.class)
@WebMvcTest(AResource.class)
public class AResourceTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private AResource aResourceMock;

    @Test
    public void testPOST() throws Exception {
        Response.ResponseBuilder created = Response.created(UriBuilder.fromPath("/123").build());
        when(aResourceMock.create(123)).thenReturn(created.build());

        mockMvc.perform(MockMvcRequestBuilders
                .request(HttpMethod.POST, "/account")
                .content("{\t\"id\": \"123\"}"))
                .andExpect(MockMvcResultMatchers.status().isCreated());
    }
}

Any idea how to properly mock my response? Thanks

EDIT: stacktrace bellow

java.lang.RuntimeException: java.lang.ClassNotFoundException: com.sun.ws.rs.ext.RuntimeDelegateImpl

    at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:122)
    at javax.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:91)
    at javax.ws.rs.core.UriBuilder.newInstance(UriBuilder.java:69)
    at javax.ws.rs.core.UriBuilder.fromPath(UriBuilder.java:111)
    at com.powerman.RestController.AResourceTest.canPOST(AResourceTest.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.ClassNotFoundException: com.sun.ws.rs.ext.RuntimeDelegateImpl
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at javax.ws.rs.ext.FactoryFinder.newInstance(FactoryFinder.java:62)
    at javax.ws.rs.ext.FactoryFinder.find(FactoryFinder.java:155)
    at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:105)
    ... 34 more

I will add my dependencies in case they help at all:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.0.1.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>21.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.9.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
            <version>1.17.1</version>
        </dependency>
    </dependencies>
Rakim
  • 1,087
  • 9
  • 21
  • 40

2 Answers2

1

I'm not sure what you are trying to do and why you would want to mock the Response. When you are running a a test for your resources, generally wha you want to do is run integration tests. What this entails is making an actual request to the endpoint using a client library. MockMvc only works when you are using Spring MVC as the REST framework. But in your case, you are using Jersey (I assume, based on the tag in your post.

As far as the client library, you can use the Jersey/JAX-RS Client API or you can use Spring Boot's TestRestTemplate, like they do in the official Spring Boot/Jersey sample project. For checking the created URI for the POST/Created resource, what you should be doing is checking the Location header in the response. This is was is set on the server side when you do Response.created(URI). For example, say this is your resource

@Path("customers")
public class CustomersResource {

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createCustomer(Customer customer, @Context UriInfo uriInfo) {
        int customerId = // create customer and get the resource id
        UriBuilder builder = uriInfo.getAbsolutePathBuilder();
        builder.path(Integer.toString(customerId));
        return Response.created(builder.build()).build();
    }
}

Jersey will automatically set the Location to value of the URI passed to created()

Then (if you're using the Jersey Client), then your test might look something like

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ExampleResourceTest {

    @LocalServerPort
    private int port;

    private Client client = ClientBuilder.newClient();


    @Test
    public void testCustomerLocationOnPost() {
        Customer customer = new Customer("Jane", "Doe");

        URI resourceUri = UriBuilder.fromUri("http://localhost")
                .port(port).path("api").path("customers").build();

        Response response = client.target(resourceUri).request().post(Entity.json(customer));

        // test that we get the correct status code
        assertThat(response.getStatus())
                .isEqualTo(Response.Status.CREATED.getStatusCode());

        // test that the location header is correct.
        assertThat(response.getHeaderString(HttpHeaders.LOCATION))
                .isEqualTo(UriBuilder.fromUri(resourceUri).path("123").build().toString());
    }
}

As far as the dependencies, to use Jersey with Spring Boot you need to use the spring-boot-started-jersey dependency. Also get rid of the jersey-core dependency you have. See the linked project above for complete example.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • that is causing a `InjectionManagerFactory not found` when doing the post request. Looking into that – Rakim May 11 '18 at 20:37
  • It has to do with the dependenc(ies) version you are using with Jersey. This problem started in 2.26 and to fix it you need to add jersey-hk2 dependency. – Paul Samsotha May 11 '18 at 20:43
  • looking into https://stackoverflow.com/questions/44088493/jersey-stopped-working-with-injectionmanagerfactory-not-found?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa that mentions that. However, I am back to `java.lang.ClassNotFoundException: org.glassfish.jersey.client.JerseyClientBuilder` on the `ClientBuilder.newClient();`.. I don't know why it's so messy. What's the point of spring-boot if not to make that easier :/ – Rakim May 11 '18 at 20:46
  • 1
    You need to be using `spring-boot-starter-jersey`. And get rid of `jersey-core`. Look at the official example I linked to. – Paul Samsotha May 11 '18 at 20:53
  • [Here's](https://github.com/psamsotha/jersey-boot-upload-example) another simple application setup (you just need to add the spring-boot-starter-test to it). And here are the [docs](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-jersey). It's actually pretty simple. It's only complicated if you don't know what you're doing and reading the wrong learning resources. – Paul Samsotha May 11 '18 at 21:04
  • I was following the tutorial from spring.io anyway, it seems to have done the job. Thanks. Do you mind adding the comment to your main answer so I can accept it? – Rakim May 11 '18 at 21:31
0

Try adding to your pom.xml:

        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>2.27</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-common</artifactId>
            <version>2.27</version>
            <scope>provided</scope>
        </dependency>
guilhebl
  • 8,330
  • 10
  • 47
  • 66