模拟EntityManager

时间:2015-04-06 10:26:59

标签: 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不为null(还有其他参数REPLICATION_DOMAIN,                 实体类中的SOURCE_NODE_INDEX,NODE_CHANGE_NUMDER不为null,而em.createNamedQuery(“NodeChangeDelta.findIdByNaturalKey”,String.class)为空。

2 个答案:

答案 0 :(得分:1)

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

  

这不是一个强硬路线,但越过这条线可能会产生影响! (很有可能。)

     
      
  1. 想象一下嘲笑第三方lib的代码。在第三个库的特定升级之后,逻辑可能会改变一点,但测试套件将执行得很好,因为它被嘲笑。所以稍后,认为一切都很好,毕竟构建墙是绿色的,软件已经部署并且...... Boom
  2.   
  3. 这可能表示当前设计与第三方库没有足够的分离。
  4.   
  5. 另一个问题是第三方lib可能很复杂,需要很多模拟才能正常工作。这会导致过度指定的测试和复杂的固定装置,这本身就会损害紧凑和可读目标。或者由于模拟外部系统的复杂性而导致的测试不足以覆盖代码。
  6.         

    相反,最常见的方法是围绕外部lib /系统创建包装器,尽管应该意识到抽象泄漏的风险,其中过多的低级API,概念或异常超出了包装器的边界。为了验证与第三方库的集成,编写集成测试,并使它们尽可能紧凑和可读。

您没有控件的模拟类型可以被视为(模拟)反模式。虽然EntityManager几乎是标准,但是不应该考虑在即将发布的JDK / JSR版本中不会有任何行为更改(它已经在API的其他部分发生了很多次,只是看看JDK发行说明)。此外,真正的实施可能会对他们的行为产生微妙的影响,很难被嘲笑,测试可能是绿色的,但生产的雄猫却着火了(真实的故事)。

我的观点是,如果代码需要模拟我不拥有的类型,那么设计应该更改 asap ,因此我,我的同事或此代码的未来维护者不会涉及这些陷阱。

此外,wiki链接到其他博客条目,这些条目描述了当他们尝试模拟他们无法控制的类型时所遇到的问题。

相反,我真的建议每个人在测试与其他系统的集成时不要使用mock 。我相信对于数据库的东西,Arquillian是可行的,项目似乎非常活跃。


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

答案 1 :(得分:0)

在Mockito中,未明确配置的模拟上的任何方法调用始终返回null。因此,在findIdByNaturalKey中,em.createNamedQuery正在返回null,因此NPE会在setParameter上返回。您需要将其配置为RETURN_MOCKS

另外,我不确定@InjectMocks是否支持@PersistenceContext。如果没有,则em可能为空。如果有,请告诉我,以上是您的问题。