JAXRS + JerseyTest testing a REST Service

2019-03-01 03:16发布

问题:

I've created a Rest service with four methods, GET,POST,UPDATE and DELETE. These methods make connections to a Database to retrieve and store data.

Now I want to test each method. I've used the Jersey Test Framework for this. And it is working as long as I remove the code what actually makes the call to the database. When I leave the code that makes the call to the database it throws an exception that it could not connect to the database.

EDIT: I have done some research and used dependancy injection. The db calls are moved to a separate class but I'm still doing something wrong.

DatabaseResults. In this class the call to the DB is made.

public class DatabaseResults {

private final String getQuery = "SELECT * FROM movies";
private Connection connection = null;
private PreparedStatement pstmt = null;
private final ArrayList<Movie> jsonList = new ArrayList<>();

public JSONObject getAllMovies() throws SQLException {

    try {
        ComboPooledDataSource dataSource = DatabaseUtility.getDataSource();
        connection = dataSource.getConnection();

        pstmt = connection.prepareStatement(getQuery);
        ResultSet rs = pstmt.executeQuery();

        while (rs.next()) {
            jsonList.add(new Movie(rs.getString(1), rs.getString(2), rs.getString(4), rs.getString(3)));
        }

    } catch (SQLException ex) {
        System.out.println(ex);
        System.out.println("Could not retrieve a connection");
        connection.rollback();
    } finally {
        connection.close();
    }

    JSONObject jsonObject = new JSONObject();
    jsonObject.put("movies", jsonList);

    return jsonObject;
    }
}

MoviesResource that contains the REST methods

@Path("movies")
public class MoviesResource {

....
private DatabaseResults dbResults = null;

public MoviesResource() {
    this(new DatabaseResults());
}

MoviesResource(DatabaseResults dbr){
     this.dbResults = dbr;

}
....
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllMovies() throws JSONException, SQLException {

    return Response.status(200).entity(dbResults.getAllMovies().toString()).build();
}

The Test class

@RunWith(MockitoJUnit44Runner.class)
public class MovieResourceTest extends JerseyTest {

JSONObject jsonObject = new JSONObject();

@Mock
DatabaseResults dbr;  

@Before
public void setup() throws SQLException{
    jsonObject.put("id", "hello");

    when(dbr.getAllMovies()).thenReturn(jsonObject);
}        

Client client = ClientBuilder.newClient();
WebTarget target = client
        .target("http://localhost:9998/RestServiceMovies/resources");

@Override
protected Application configure() {
    return new ResourceConfig(MoviesResource.class);
}

@Test
public void getAllMoviesTest() throws SQLException {

    String responseGetAllMovies = target("/movies").request().get(String.class);

    Assert.assertTrue("hello".equals(responseGetAllMovies));
}

At this moment I can run the tests but still when I test the getAllMovies() method it makes a call to the real database instead of returning the jsonObject.

I have the feeling that a connection is missing between the mock object and the constructor from the MovieResource class?

回答1:

When you register your resource as a class

new ResourceConfig(MoviesResource.class)

you are telling Jersey to create the instance. If you don't have any DI configured, it will just call the no-arg constructor. In your no-arg constructor, you are just creating the service yourself. It knows nothing about your mock.

What you should do instead is register the resource class as an instance. That way you can pass the mock to the constructor.

MockitoAnnotations.initMocks(this);
return new ResourceConfig()
        .register(new MoviesResource(dbr));

Don't use the Mockito runner. Instead use the MockitoAnnotations.initMocks method. That way you control when the @Mocks are injected. If you use the runner, the injection will not happen in time, as the the configure method is called by the framework before the Mockito injection happens.