使用 mockito 模拟实体管理器并测试无状态 EJB

Using mockito to mock the entity manager and test a stateless EJB

所以我是使用 mockito 进行测试的新手,我已经查阅了一些教程,但我理解起来很慢。 我有一个由 EJB 支持的端点。我想测试中的方法 EJB。我尝试了很多选项,显然我得到了一个空指针异常。 (我知道什么是 空指针异常是)。模拟的实体管理器不会保留对象。所以它给了我 当我尝试对空列表执行任何操作时出现空指针异常。请任何人都可以展示 我如何解决这个问题,或者指出我可以阅读的任何详细文章或教程以促进我的学习。谢谢大家。

这是我的ejb。

@Stateless
public class CustomerHandler {

  @PersistenceContext
  private EntityManager em;

  public Response borrowMovie(List<RequestMovieDto> borrow) {
    borrow.forEach(movies -> {
        final Movie movie = findMovieByName(movies.getName());
        movie.setAvailableCopies((movie.getAvailableCopies()) - movies.getAmount());
        em.merge(movie);
    });

    return Response.ok(borrow).build();
  }

  public Movie findMovieByName(String name) {
    return em.createQuery("SELECT m FROM Movie m "
        + "WHERE m.name = :name", Movie.class)
        .setParameter("name", name)
        .getSingleResult();
  }
} 

and this is my test class

@RunWith(MockitoJUnitRunner.class)
public class MovieHandlerTest {

  @Mock
  EntityManager manager;

  private List<RequestMovieDto> request;

  @InjectMocks
  CustomerHandler handler;

  @Before
  public void setUp() {
    final Movie first = new Movie();
    first.setName("MISSION IMPOSSIBLE");
    first.setAvailableCopies(10);
    first.setIsbn("ABC788900");

    manager.persist(first);

    final Movie second = new Movie();
    first.setName("JAMES BOND");
    first.setAvailableCopies(10);
    first.setIsbn("ABC788999");

    manager.persist(second);

    final Movie last = new Movie();
    first.setName("HARRY POTTER");
    first.setAvailableCopies(10);
    first.setIsbn("ABC7882000");

    manager.persist(last);

  }

  @Test
  public void borrowMovie() {
    RequestMovieDto first = new RequestMovieDto();
    first.setName("MISSION IMPOSSIBLE");
    first.setAmount(2);

    RequestMovieDto second = new RequestMovieDto();
    second.setName("JAMES BOND");
    second.setAmount(1);

    request = Arrays.asList(first, second);
     final var response = handler.borrowMovie(request);

    assertEquals(Response.Status.OK, response.getStatusInfo().toEnum());
  }
}

为避免 NullPointerException,您可以尝试在 MovieHandlerTest class 中添加以下代码:

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;

[..]

@Mock
EntityManager manager;

@Mock
TypedQuery<Movie> query;

[..]

@Before
public void setUp() {
    final Movie first = new Movie();
    first.setName("MISSION IMPOSSIBLE");
    first.setAvailableCopies(10);
    first.setIsbn("ABC788900");

    final Movie second = new Movie();
    first.setName("JAMES BOND");
    first.setAvailableCopies(10);
    first.setIsbn("ABC788999");

    final Movie last = new Movie();
    first.setName("HARRY POTTER");
    first.setAvailableCopies(10);
    first.setIsbn("ABC7882000");

    when(manager.createQuery(any(String.class), eq(Movie.class))).thenReturn(query);
    when(query.setParameter(any(String.class), any(String.class))).thenReturn(query);
    when(query.getSingleResult()).thenReturn(first, second, last);
}

但是 "Response.ok(borrow).build()"

又出现了另一个问题

您有 3 个解决方案:

  1. 重构代码以引入执行借用工作流的 class 和 returns 模型,然后对该 class
  2. 进行单元测试
  3. 添加带有测试范围的 JAX-RS 实现,例如 Jersey
  4. 由于无法单独使用 Mockito 模拟静态方法,您可以尝试 添加另一个测试库,例如 PowerMockito,并模拟 Reponse 静态方法调用。

对于方案二,可以添加如下maven依赖:

    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>2.13</version>
        <scope>test</scope>
    </dependency>