JAXRS + JerseyTest 测试 REST 服务
JAXRS + JerseyTest testing a REST Service
我用四种方法创建了一个 Rest 服务,GET、POST、UPDATE 和 DELETE。
这些方法与数据库建立连接以检索和存储数据。
现在我想测试每个方法。为此,我使用了 Jersey 测试框架。只要我删除实际调用数据库的代码,它就会工作。当我离开调用数据库的代码时,它会抛出无法连接到数据库的异常。
编辑:我做了一些研究并使用了依赖注入。数据库调用已移至单独的 class,但我仍然做错了。
数据库结果。在此 class 中调用了数据库。
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;
}
}
包含 REST 方法的 MoviesResource
@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();
}
测试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));
}
此时我可以 运行 测试,但是当我测试 getAllMovies()
方法时,它仍然调用真实数据库而不是返回 jsonObject
.
我感觉模拟对象和 MovieResource 的构造函数之间缺少连接 class?
当您将资源注册为 class
new ResourceConfig(MoviesResource.class)
您是在告诉 Jersey 创建实例。如果您没有配置任何 DI,它只会调用无参数构造函数。在您的无参数构造函数中,您只是自己创建服务。它对你的模拟一无所知。
您应该做的是将资源 class 注册为一个实例。这样你就可以将模拟传递给构造函数。
MockitoAnnotations.initMocks(this);
return new ResourceConfig()
.register(new MoviesResource(dbr));
不要使用 Mockito 运行器。而是使用 MockitoAnnotations.initMocks
方法。这样你就可以控制何时注入 @Mock
。如果使用 runner,注入将不会及时发生,因为 configure
方法在 Mockito 注入发生之前被框架调用。
我用四种方法创建了一个 Rest 服务,GET、POST、UPDATE 和 DELETE。 这些方法与数据库建立连接以检索和存储数据。
现在我想测试每个方法。为此,我使用了 Jersey 测试框架。只要我删除实际调用数据库的代码,它就会工作。当我离开调用数据库的代码时,它会抛出无法连接到数据库的异常。
编辑:我做了一些研究并使用了依赖注入。数据库调用已移至单独的 class,但我仍然做错了。
数据库结果。在此 class 中调用了数据库。
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;
}
}
包含 REST 方法的 MoviesResource
@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();
}
测试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));
}
此时我可以 运行 测试,但是当我测试 getAllMovies()
方法时,它仍然调用真实数据库而不是返回 jsonObject
.
我感觉模拟对象和 MovieResource 的构造函数之间缺少连接 class?
当您将资源注册为 class
new ResourceConfig(MoviesResource.class)
您是在告诉 Jersey 创建实例。如果您没有配置任何 DI,它只会调用无参数构造函数。在您的无参数构造函数中,您只是自己创建服务。它对你的模拟一无所知。
您应该做的是将资源 class 注册为一个实例。这样你就可以将模拟传递给构造函数。
MockitoAnnotations.initMocks(this);
return new ResourceConfig()
.register(new MoviesResource(dbr));
不要使用 Mockito 运行器。而是使用 MockitoAnnotations.initMocks
方法。这样你就可以控制何时注入 @Mock
。如果使用 runner,注入将不会及时发生,因为 configure
方法在 Mockito 注入发生之前被框架调用。