i am trying to test that a method creates an object. i have it almost working using PowerMockito.verifyNew().withArguments() however, the Arguments that are passed to the constructor are an object and an ArrayList of objects. the output of the test is: Actual
invocationSubstitute.performSubstitutionLogic(
1,
6,
11,
13,
[au.edu.sccs.csp3105.NBookingPlanner.Person@2449cff7],
au.edu.sccs.csp3105.NBookingPlanner.Room@62da83ed,
"description"
);
Expected
invocationSubstitute.performSubstitutionLogic(
1,
6,
11,
13,
[au.edu.sccs.csp3105.NBookingPlanner.Person@40bffbca],
au.edu.sccs.csp3105.NBookingPlanner.Room@42a9a63e,
"description"
);
i can see that the problem is the objects are the same type but not the same object, is there a way of saying the Expected object is of the correct type?
test:
@RunWith(PowerMockRunner.class)
@PrepareForTest({Planner.class, Meeting.class})
public class MonthInput {
Planner planner;
@Rule
public final TextFromStandardInputStream systemInMock = emptyStandardInputStream();
@Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
@SuppressWarnings("deprecation")
@Before
public void setup() throws Exception {
Meeting meetingMock = Mockito.mock(Meeting.class);
PowerMockito.whenNew(Meeting.class).withAnyArguments().thenReturn(meetingMock);
}
@Test
public void MonthInputofless5() throws Exception {
// make spy
planner = Mockito.spy(Planner.class);
//override main menu with do nothing
Mockito.doNothing().when(planner).mainMenu();
//provide inputs
systemInMock.provideLines("1","6","11","13","ML13.218","Mark Colin","done","description");
//set expected outputs
ArrayList<Person> attendees = new ArrayList<Person>();
attendees.add(new Person("Mark Colin"));
Room where = new Room("ML13.218");
//call the method
planner.scheduleMeeting();
//set passing terms
PowerMockito.verifyNew(Meeting.class).withArguments(1,6,11,13,attendees,where,"description");
}
A verify simple fix on your classes: implement hashCode
and equals
on Person
and Room
so Powermock verification can actually compare two objects for equality and not just rely on the object reference.
If you don't want to fix your code, but the test, you could either use a Mockito matcher (i.e. org.mockito.Matchers.eq
or org.mockito.Matchers.any
). But please note, that eq
relies on equals
and won't work unless you implement it (see above). But any
would match any object of that type (surprise!)
PowerMockito.verifyNew(Meeting.class)
.withArguments(eq(1),eq(6),eq(11),eq(13),
any(List.class),
any(Room.class),
eq("description"));
If the actual value matters, you could use an ArgumentCapture instead of a matcher and check the captured value.
Theoretically, it should look like this:
final ArgumentCaptor<Person> personCaptor = forClass(Person.class);
final ArgumentCaptor<Room> roomCaptor = forClass(Room.class);
PowerMockito.verifyNew(Planner.class)
.withArguments(eq(1),eq(6),eq(11),eq(13),
personCaptor.capture(),
roomCaptor.capture(),
eq("description"));
final Person passedParam = personCaptor.getValue();
//do your comparison here
But I didn't get the capture example running, so maybe that's only possible with plain Mockito.
All that said, you should verify your overall approach. Using Whitebox-Testing may create very brittle tests and won't help much in refactoring and may further foster bad class design.
The imprudent use of Powermockito is an anti-pattern. It's a very mighty tool for testing the untestable, i.e. badly designed legacy code from the dark ages of the internet where the homepages were handcrafted HTMLs and full of wobbly GIFs. Don't use it for new, greenfield projects. Just don't.
Instead try focusing on the observable result of any action. Invoking the scheduleMeeting()
method may produce any result that is more easy to check - it's the result that matters, not the way to get they. Trust me, not a single user gets happier when a constructor was called.
Results could be