I want to test a method of a class. This class has 1 constructor with 3 parameters, which I can only supply one of the 3 requested.
I cannot mock the object because I am testing for invalid use, so I need the test to expect an exception. Mocking the object results in no exception being thrown.
The problem with mocking
public class NodeHandler {
private List<Node> nodes;
private Node currentNode;
public NodeHandler(List<Node> nodes, Object obj1, Object obj2) {
this.nodes = nodes;
//uses obj1 and obj2; they cannot be null
}
public void initCurrentNode() {
for(Node node : nodes) {
if(node.canProcess()) {
currentNode = node;
return;
}
}
throw new IllegalStateException("No nodes can be processed");
}
}
The method I'm testing only depends on List<Node> nodes
.
I manually initialize this myself (in the test) by accessing the field through reflection and setting the value:
public class MyTest {
private NodeHandler mocked = Mockito.mock(NodeHandler.class);
@Test(expected = IllegalStateException.class)
public void testInvalidUsage() throws Exception {
List<Node> nodes = new ArrayList<>();
nodes.add(FirstNode.class.newInstance());
nodes.add(SecondNode.class.newInstance());
Field field = NodeHandler.class.getDeclaredField("nodes");
field.setAccessible(true);
field.set(mocked, nodes);
try {
method.invoke(manager);
} catch (IllegalAccessException | IllegalArgumentException e) {
throw new InvocationTargetException(e);
} catch (InvocationTargetException e) {
throw (IllegalStateException) e.getTargetException();
}
}
public static class FirstNode extends Node {
public boolean canProcess() { return false; }
public void process() { }
}
public static class SecondNode extends Node {
public boolean canProcess() { return false; }
public void process() { }
}
}
This test fails, since it expects an IllegalStateException
, but one is not thrown (due to the mock object).
The nasty work-around
I could declare a private nullary constructor in NodeHandler
, set it to accessible. This will allow me to create an object whose methods may throw exceptions:
class NodeHandler {
//...
private NodeHandler() { }
//...
}
public class MyTest {
private NodeHandler manager;
@Before
public void init() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Constructor<?> constructor = NodeHandler.class.getDeclaredConstructor();
constructor.setAccessible(true);
manager = (NodeHandler) constructor.newInstance();
}
@Test(expected = IllegalStateException.class)
public void testInvalidUsage() throws Exception {
//same as before
}
}
This give me the results I want, but seems "hackish". I don't want to be required to declare a private constructor in every class I want to test.
My Question
Without being forced to declare a private nullary constructor, how could I test a method that's expected to throw an exception from an object without being required to fill all the parameters of the constructor for that object? Is there a way I can mock an object while still accounting for exceptions thrown by methods?
Aucun commentaire:
Enregistrer un commentaire