问题
I am getting NPE while mocking EntityManager, below is my code,
@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);
}
}
My Entity Class
@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);
}
}
My Test Class
@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);
}
}
While debugging em is not null (also other arguments REPLICATION_DOMAIN, SOURCE_NODE_INDEX, NODE_CHANGE_NUMDER not null) in Entity class, whereas em.createNamedQuery("NodeChangeDelta.findIdByNaturalKey", String.class) is null.
回答1:
On the 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.)
- 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
- It may be a sign that the current design is not decoupled enough from this third party library.
- 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 type that you don't have the control can be considered a (mocking) anti-pattern. While EntityManager
is pretty much standard, one should not consider there won't be any behavior change in upcoming JDK / JSR releases (it already happened numerous time in other part of the API, just look at the JDK release notes). Plus the real implementations may have subtleties in their behavior that can hardly be mocked, tests may be green but the production tomcats are on fire (true story).
My point is that if the code needs to mock a type I don't own, the design should change asap so I, my colleagues or future maintainers of this code won't fall in these traps.
Also the wiki links to other blogs entries describing issues they had when they tried to mock type they didn't have control.
Instead I really advice everyone to don't use mock when testing integration with another system. I believe for database stuff, Arquillian is the thing to go, the project appears to be quite active.
Adapted from my answer : https://stackoverflow.com/a/28698223/48136
回答2:
In Mockito, any method invocation on a mock that is not explicitly configured, always returns null
. Therefore in findIdByNaturalKey
, em.createNamedQuery
is returning null
and so NPE on setParameter
. You need to configure it to RETURN_MOCKS.
Also, I am not sure if @InjectMocks
supports @PersistenceContext
. If it does not then em
is probably null. If it does, please let me know and the above is your issue.
来源:https://stackoverflow.com/questions/29469442/mocking-entitymanager