Say I want to test the behavior of the GUI while I follow a PassiveView approach. I also use the command pattern to handle the actions of the user. So given a PersonView and a PersonService with a savePerson(firstName,lastName)
method, my command looks like:
class SavePersonCommand{
public SavePersonCommand(PersonService personService,PersonView personView,
CommandExecutor commandExecutor) {
//args go as fields
}
void execute() {
String firstName = personView.getFirstName().trim();
String lastName = personView.getLastName().trim();
personService.store(firstName, lastName);
//trimmed values will be shown to view
commandExecutor.execute(RefreshPersonView.class);
}
}
Mocking PersonService which talks to a database feels a normal thing to do. Mocking (fake-test double-mock whatever) view is what Martin Fowler suggests in the link above. However, the thing I end up having - a class tested only given mocks feels weird. Do I really benefit from that? Is it a code smell? Is it a normal phenomenon? I am also reading around a lot of discussions of kind "mocking is bad". Am I mocked by my mocks? (basically at 30:44)
From my perspective the tests of the commands which is essentially the whole GUI logic are simple. Clean, fast, straight forward and GUI framework/library independent. SavePersonCommand
copies the X values from fields of the view and passes them to the service. I also, do not have to worry for other possible dependencies of the view. Like SpellCheckers, Painters or things like that.
As far as I know, we should test the code we fear. The GUI logic (the commands) is certainly something I am afraid of. But is it this a "correct" way of unit testing them?
And for the record, here is how my test would look like:
class SavePersonCommandShould{
@Test
void save_person_with_properties_from_view_and_then_refresh_the_view() {
PersonService service = mock(PersonService.class);
PersonView view = mock(PersonView.class);
CommandsExecutor executor = mock(CommandExecutor.class);
when(view.getFirstName()).thenReturn("mike ");
when(view.getLastName()).thenReturn("thedeveloper"+System.lineSeparator());
new SavePersonCommand(service,view,executor).execute();
verify(service).store(eq("mike"),eq("thedeveloper"));
verify(executor).execute(eq(RefreshPersonView.class));
}
}
On the other side, if I use the implementation of the view I have the benefit of testing more things at the same time. Things like "do the personView.getFirstName()
returns the value of the fistNameTextField". Plus, the testing code is closer to what happens in production. But at the same time, I get dirty tests since views may require complex initiation.