testing - 模拟实体管理器

标签 testing junit mockito entitymanager

我在模拟 EntityManager 时遇到 NPE,下面是我的代码,

@Stateless
public class NodeChangeDeltaQueryBean implements NodeChangeDeltaQueryLocal {

    @PersistenceContext
    private EntityManager em;
    @Override
    public String findIdByNaturalKey(final String replicationDomain, final int sourceNodeIndex,
                                     final int nodeChangeNumber) {
        List<String> result =
            NodeChangeDelta.findIdByNaturalKey(this.em, replicationDomain, sourceNodeIndex,
                nodeChangeNumber).getResultList();
        return result.isEmpty() ? null : result.get(0);
    }
}

我的实体类

@Entity
public class NodeChangeDelta implements Serializable, Cloneable, GeneratedEntity, KeyedEntity<String> {

public static TypedQuery<String> findIdByNaturalKey(final EntityManager em, final String replicationDomain, final int sourceNodeIndex, final int nodeChangeNumber) {
        return em.createNamedQuery("NodeChangeDelta.findIdByNaturalKey", String.class)
            .setParameter("replicationDomain", replicationDomain)
            .setParameter("sourceNodeIndex", sourceNodeIndex)
            .setParameter("nodeChangeNumber", nodeChangeNumber);
    }
}

我的测试类

@RunWith(MockitoJUnitRunner.class)
public class NodeChangeDeltaQueryBeanTest {

    @InjectMocks
    NodeChangeDeltaQueryBean nodeChangeDeltaQueryBean;

    @Mock
    EntityManager em;

@Test
    public void testFindIdByNaturalKey() {
        this.addNodeChangeDelta();
        this.nodeChangeDeltaQueryBean.findIdByNaturalKey(this.REPLICATION_DOMAIN,
            this.SOURCE_NODE_INDEX, this.NODE_CHANGE_NUMDER);
    }
}

虽然调试 em 不为空(还有其他参数 REPLICATION_DOMAIN, 实体类中的 SOURCE_NODE_INDEX, NODE_CHANGE_NUMDER 不为空),而 em.createNamedQuery("NodeChangeDelta.findIdByNaturalKey", String.class) 为空。

最佳答案

在 mockito wiki 上:Don't mock types you don't own !

This is not a hard line, but crossing this line may have repercussions! (it most likely will.)

  1. Imagine code that mocks a third party lib. After a particular upgrade of a third library, the logic might change a bit, but the test suite will execute just fine, because it's mocked. So later on, thinking everything is good to go, the build-wall is green after all, the software is deployed and... Boom
  2. It may be a sign that the current design is not decoupled enough from this third party library.
  3. Also another issue is that the third party lib might be complex and require a lot of mocks to even work properly. That leads to overly specified tests and complex fixtures, which in itself compromises the compact and readable goal. Or to tests which do not cover the code enough, because of the complexity to mock the external system.

Instead, the most common way is to create wrappers around the external lib/system, though one should be aware of the risk of abstraction leakage, where too much low level API, concepts or exceptions, goes beyond the boundary of the wrapper. In order to verify integration with the third party library, write integration tests, and make them as compact and readable as possible as well.

你没有控制权的 Mock 类型可以被认为是 (mocking) 反模式。虽然 EntityManager 几乎是标准,但不应认为在即将发布的 JDK/JSR 版本中不会有任何行为变化(它已经在其他部分发生过多次) API,只需查看 JDK 发行说明)。此外,真实的实现在行为上可能有难以模仿的微妙之处,测试可能是绿色的,但生产 tomcat 却着火了(真实故事)。

我的观点是,如果代码需要模拟一个我不拥有的类型,设计应该尽快改变,这样我、我的同事或该代码的 future 维护者就不会落入这些陷阱。

该 wiki 还链接到其他博客条目,描述了他们在尝试模拟他们无法控制的类型时遇到的问题。

相反,我真的建议大家在测试与另一个系统的集成时不要使用模拟。我相信对于数据库的东西,Arquillian是要进行的事情,该项目似乎非常活跃。


改编 self 的回答:https://stackoverflow.com/a/28698223/48136

关于testing - 模拟实体管理器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29469442/

相关文章:

session - 使用断言 session 值的 CakePHP 测试

testing - 使 Grails Geb 测试具有事务性

java - 使用 JMockit 模拟和验证 SLF4J

java - 使用 Powermock 测试 Spring Controller

visual-studio-2008 - 在 Visual Studio 2008 VC++ 中进行单元测试的程序(.exe 文件)

testing - 你如何配置 babel 以在不同的环境中以不同的配置运行

spring - 使用 Spring MVC Test 测试 Spring MVC @ExceptionHandler 方法

java - 单元测试困境

java - 存储库到服务的依赖关系未得到满足

junit - 如何 jmock Final 类(class)