【问题标题】:JAXRS + JerseyTest testing a REST ServiceJAXRS + JerseyTest 测试 REST 服务
【发布时间】:2016-06-16 18:05:48
【问题描述】:

我创建了一个带有四种方法的 Rest 服务,GET、POST、UPDATE 和 DELETE。 这些方法与数据库建立连接以检索和存储数据。

现在我想测试每种方法。我为此使用了 Jersey 测试框架。只要我删除实际调用数据库的代码,它就可以工作。当我离开调用数据库的代码时,它会抛出一个无法连接到数据库的异常。

编辑:我做了一些研究并使用了依赖注入。 db 调用被移到一个单独的类,但我仍然做错了什么。

数据库结果。在这个类中调用了数据库。

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();
}

测试类

@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 类的构造函数之间缺少连接?

【问题讨论】:

  • 这很难测试,因为您的 REST 服务违反了单一职责原则。如果您将数据库逻辑封装在另一个类中,那么您可以轻松地模拟该类以单独测试您的 REST 服务。另一种常见的方法是通过单元测试,只运行集成测试;即配置一个内存数据库,不要模拟任何东西。
  • 好的。所以我将我的数据库逻辑封装在一个单独的类中。我想用 Mockito 模拟数据,但实际上我不确定如何说我的 getAllMovies 方法应该使用模拟类...
  • 有几种方法可以解决这个问题。我假设您的 REST 服务可以访问某些私有字段,例如moviesDatabase。您想模拟此对象或其方法,因此您需要某种方式告诉 REST 服务使用哪个 moviesDatabase。您的选择是:(1)构造函数注入(2)使字段包私有并直接设置它(3)使用像Dagger或Weld这样的花哨的依赖注入框架。
  • 请更新您的问题,将数据库逻辑移至单独的类,以便我们了解如何为您提供帮助。

标签: java rest jersey jax-rs jersey-test-framework


【解决方案1】:

当您将资源注册为类时

new ResourceConfig(MoviesResource.class)

您是在告诉 Jersey 创建实例。如果您没有配置任何 DI,它只会调用无参数构造函数。在您的无参数构造函数中,您只是自己创建服务。它对你的模拟一无所知。

您应该做的是将资源类注册为实例。这样您就可以将模拟传递给构造函数。

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

不要使用 Mockito 跑步者。而是使用MockitoAnnotations.initMocks 方法。这样您就可以控制何时注入 @Mocks。如果使用 runner,则注入不会及时发生,因为 configure 方法是在 Mockito 注入发生之前由框架调用的。

【讨论】:

  • 谢谢!我删除了 Mockito 跑步者并添加了 MockitoAnnotations.initMocks(this); return new ResourceConfig().register(new MoviesResource(dbr)) 代码。
  • 你的权利,应该是initMocks,而不是init :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多