Testing a Presenter in a Model-View-Presenter application

98 views
Skip to first unread message

"Ionuț G. Stan"

unread,
Jan 22, 2011, 3:12:52 PM1/22/11
to GOOS
Hi,

I'm trying to TDD a Model-View-Presenter application (just for context,
this is a GWT application). The problem I have is that there appears to
be a circular dependency between the presenter and the view, which can
be solved by using events, but then, I have no idea how to trigger off
the assertion process in the test without using a concrete
implementation of the event registration process. Let me explain it with
code, I think you'll understand better.

First, the test.

class GalleryPresenterTest {

// In a gallery of pictures, trying to delete an item will
// prompt the user with some sort of confirmation dialog.
// The deletion event is triggered by the view.
@Test
public void deletingAnItemRequiresConfirmation() {
final View view = context.mock(View.class);

context.checking(new Expectations() {{
oneOf(view).confirmDeletion();
}});

GalleryPresenter presenter = new GalleryPresenter(view);

presenter.onDeleteItem();
}
}


The problem above is that presenter.onDeleteItem() must be called by the
view, and the presenter has to call view.confirmDeletion(). So, the
concrete implementation of View needs a reference to the presenter.
Passing the presenter to the View constructor is impossible because the
presenter constructor requires the same instance of the view. This is
the problem of cyclic dependency I was talking about at the beginning. I
could create a setPresenter() on the view, but it smells to me.

View view = new View();
Presenter presenter = new Presenter(view);
view.setPresenter(presenter);


The solution appears to be events. And what I did (before writing this
email) was this:

interface DeleteItemListener {
void onDeleteItem();
}

class Presenter {
public Presenter(View view) {
view.addDeleteItemListener(new DeleteItemListener() {
public void onDeleteItem() {
onDeleteItem();
}
});
}

// This class could implement DeleteItemListener just as well.
public void onDeleteItem() {}
}

Presenter presenter = new Presenter(new View());


So, first of all, there's a smell in the above solution in that no
method of the presenter is used by someone else after its instantiation.
The presenter has basically no public interface.

The second problem is that, in the test, I can't tell the view to fire
the event because the view is a mock. The hack was to make
onDeleteItem() public, but that won't stop anybody from deleting the
event registration code, and the test will still pass.

Now, while writing this email, I realized that maybe the registration
process should be extracted into a separate component, outside the
presenter. Some sort of an application controller, which would wire
things up at the start of the application. I'm not sure this is the
solution, but seems to be what the tests are telling to me.

class Module {
void init() {
View view = new View();
Presenter presenter = new Presenter(view);

// Here Presenter implements the required interface.
view.addDeleteItemListener(presenter);
}
}


What do you think? Is there any other solution I missed?

--
Ionuț G. Stan | http://igstan.ro

Dale Emery

unread,
Jan 22, 2011, 4:43:31 PM1/22/11
to growing-object-o...@googlegroups.com
Hi Ionuț,

> The problem above is that presenter.onDeleteItem() must be called by the view, and the presenter has to call view.confirmDeletion(). So, the concrete implementation of View needs a reference to the presenter. Passing the presenter to the View constructor is impossible because the presenter constructor requires the same instance of the view. This is the problem of cyclic dependency I was talking about at the beginning. I could create a setPresenter() on the view, but it smells to me.
>
> View view = new View();
> Presenter presenter = new Presenter(view);
> view.setPresenter(presenter);

That's what I do. What smell are you smelling?

What I like about this tactic: The presenter no longer cares where the events come from. So it now uses the view only for one purpose (confirming) instead of two (registering and confirming). So I can use a more focused interface to connect the two, like this:

class GalleryPresenter implements DeleteItemListener {
private final DeleteItemConfirmer confirmer;
public GalleryPresenter(DeleteItemConfirmer confirmer) {
this.confirmer = confirmer;
}
...
}

class View implements DeleteItemConfirmer {
...
}

View view = new View();

GalleryPresenter presenter = new GalleryPresenter(view);

view.addDeleteItemListener(presenter);

I like that the presenter is no longer coupled to the source of the events.

Dale

Aviv Ben-Yosef

unread,
Jan 22, 2011, 5:47:33 PM1/22/11
to growing-object-o...@googlegroups.com, GOOS
I too use Dale's solution and think the decoupling is quite fruitful.

I've summarized the path I went to get to this design in a blog post not long ago: http://www.codelord.net/2010/12/18/adding-goos-sauce-to-gwt-mvp/

Cheers,
Aviv

"Ionuț G. Stan"

unread,
Jan 22, 2011, 10:42:49 PM1/22/11
to growing-object-o...@googlegroups.com
Hi Dale,

On January 22, 2011 11:43 PM, Dale Emery wrote:
> Hi Ionuț,
>
>> The problem above is that presenter.onDeleteItem() must be called by the view, and the presenter has to call view.confirmDeletion(). So, the concrete implementation of View needs a reference to the presenter. Passing the presenter to the View constructor is impossible because the presenter constructor requires the same instance of the view. This is the problem of cyclic dependency I was talking about at the beginning. I could create a setPresenter() on the view, but it smells to me.
>>
>> View view = new View();
>> Presenter presenter = new Presenter(view);
>> view.setPresenter(presenter);
>
> That's what I do. What smell are you smelling?

The difference that I make between setPresenter() and
addDeleteItemListener() is that in the former case, the view can't
function if you don't call setPresenter(), as it would throw a
NullPointerException at some point. In the latter case, the view is a
fully functional object because it would keep an internal collection of
events and dispatching on an empty collection would cause no errors.

In the GOOS world this would be the difference between a Dependency and
a Notification type of peer.

I didn't really render this difference explicit in my previous email.
Sorry about that.

>
> What I like about this tactic: The presenter no longer cares where the events come from. So it now uses the view only for one purpose (confirming) instead of two (registering and confirming). So I can use a more focused interface to connect the two, like this:
>
> class GalleryPresenter implements DeleteItemListener {

> private final DeleteIte .,


> }
>
> class View implements DeleteItemConfirmer {
> ...
> }
>
> View view = new View();
> GalleryPresenter presenter = new GalleryPresenter(view);
> view.addDeleteItemListener(presenter);
>
> I like that the presenter is no longer coupled to the source of the events.

This is an interesting idea and I reckon now there are advantages to it.
Indeed, there's no rule that says the events must come from the view
that you want to modify.

Anyway, where would you place this piece of code that registers the
presenter as a listener to the view's events? And how would you call it,
because it's not really "main". Or maybe it is some sort of "main" for
an MVP cluster and together they could make up a Component or Widget.

Thanks!

"Ionuț G. Stan"

unread,
Jan 22, 2011, 10:53:13 PM1/22/11
to growing-object-o...@googlegroups.com
Hi Aviv,

On January 23, 2011 12:47 AM, Aviv Ben-Yosef wrote:
> I too use Dale's solution and think the decoupling is quite fruitful.
>
> I've summarized the path I went to get to this design in a blog post not long ago: http://www.codelord.net/2010/12/18/adding-goos-sauce-to-gwt-mvp/

I guess I have the same question for you. Where do you place that bind()
method? Is it inside the presenter? If yes, how do you trigger the event
in the test case in order to exercise the presenter code.

Thanks!

P.S. Regarding this quote from your blog post:

"Don’t be afraid to do something differently than the documentation,
especially if you gave it a fair shot and it didn’t work out."

I'm more often afraid of people that would criticize me because I didn't
do it like Google said.

Dale Emery

unread,
Jan 23, 2011, 1:54:06 AM1/23/11
to growing-object-o...@googlegroups.com
Hi Ionuț,

> Anyway, where would you place this piece of code that registers the presenter as a listener to the view's events?


For the smaller apps I tend to write, I generally I do that in the constructor of a class that represents the application as a whole. That constructor builds or gathers the parts of the app and wires them together. In a larger app, there might be different bits of code that build and configure new clusters of views and presenters at different times.

You already have some bit of code that calls constructors or setters to tell a given view about a given presenter, or vice versa. That code already knows the relationship between view and presenter. So that's where I'd do the registration.

Dale

Aviv Ben-Yosef

unread,
Jan 23, 2011, 3:46:43 AM1/23/11
to growing-object-o...@googlegroups.com
On Sun, Jan 23, 2011 at 05:53, "Ionuț G. Stan" <ionut....@gmail.com> wrote:
Hi Aviv,


On January 23, 2011 12:47 AM, Aviv Ben-Yosef wrote:
I too use Dale's solution and think the decoupling is quite fruitful.

I've summarized the path I went to get to this design in a blog post not long ago: http://www.codelord.net/2010/12/18/adding-goos-sauce-to-gwt-mvp/

I guess I have the same question for you. Where do you place that bind() method? Is it inside the presenter? If yes, how do you trigger the event in the test case in order to exercise the presenter code.


I place that method in the presenter. I create an interface between the presenter and the view and so that metods of the presenter are public. That makes testing the presenter a breeze. Also, sometimes I simply change these handler registrations to simply call a setPresenter() on the view. This is easy to test for and removes a lot of boilerplate code.

If you really are afraid the setup code will break, test it (e.g. when creating a presenter, it calls setPresenter on the view).
 
Thanks!

P.S. Regarding this quote from your blog post:

"Don’t be afraid to do something differently than the documentation, especially if you gave it a fair shot and it didn’t work out."

I'm more often afraid of people that would criticize me because I didn't do it like Google said.


You shouldn't be afraid to be criticized, it's an excellent opportunity to learn. Asking on a list is a great thing to do, and it seems like you got to a good solution by yourself!
 

--
Ionuț G. Stan  |  http://igstan.ro



--
Aviv Ben-Yosef
Miracle Max: You rush a miracle man, you get rotten miracles.

"Ionuț G. Stan"

unread,
Jan 23, 2011, 2:31:13 PM1/23/11
to growing-object-o...@googlegroups.com
Thanks for the feedback. For the moment I'll just play a little more
with my code and see what development path ends up being better. I have
a hunch that I'll end up by wiring components in the Main class of the
application though.

Uberto Barbini

unread,
Jan 23, 2011, 3:03:55 PM1/23/11
to growing-object-o...@googlegroups.com
Hi,

I'll present a speech on TDD with GWT at ACCU 2011. It was a part of a
GWT presentation I did at WebTech Milano in November 2010.
My presentation is more focused on testing the view in TDD, but of
course also Presenter is done in TDD.

The main concept is having a interface for the view so that presenter
will know nothing about visual components (buttons, grids, etc.).
There are only notifications and setters like addSaveClickHandler,
setGridHeaders, etc.

I can put some material on my (neglected) blog in the next week if
this can be useful.

cheers

Uberto

Dale Emery

unread,
Jan 23, 2011, 4:07:55 PM1/23/11
to growing-object-o...@googlegroups.com
Hi Ionuț,

> Thanks for the feedback. For the moment I'll just play a little more with my code and see what development path ends up being better.

That's definitely what I do.

> I have a hunch that I'll end up by wiring components in the Main class of the application though.

In case I wasn't clear, that's what I meant, too. My application class is the one with main() in it. I just name it after the app, instead of calling it Main.

Dale

David Peterson

unread,
Jan 24, 2011, 7:50:04 AM1/24/11
to growing-object-o...@googlegroups.com
When the application class gets unwieldy and we decide to carve it up, what would we call the pieces? I'm thinking "plugins", but there's probably a better name?

David

"Ionuț G. Stan"

unread,
Jan 24, 2011, 2:06:39 PM1/24/11
to growing-object-o...@googlegroups.com
On January 23, 2011 11:07 PM, Dale Emery wrote:
>> I have a hunch that I'll end up by wiring components in the Main class of the application though.
>
> In case I wasn't clear, that's what I meant, too. My application class is the one with main() in it. I just name it after the app, instead of calling it Main.

I know. I just wanted to say that I probably end up with the same
solution as yours.

"Ionuț G. Stan"

unread,
Jan 24, 2011, 2:11:47 PM1/24/11
to growing-object-o...@googlegroups.com
On January 24, 2011 2:50 PM, David Peterson wrote:
> When the application class gets unwieldy and we decide to carve it up,
> what would we call the pieces? I'm thinking "plugins", but there's
> probably a better name?

Widget and module are two options that come to mind.

Dale Emery

unread,
Jan 24, 2011, 4:33:27 PM1/24/11
to growing-object-o...@googlegroups.com
Hi David,

> When the application class gets unwieldy and we decide to carve it up, what would we call the pieces? I'm thinking "plugins", but there's probably a better name?

As with carving a turkey, the names of the piles depend entirely on the carving.

If you're serving a turkey dinner, you might carve the turkey into:
- A pile of white meat slices
- A pile of dark meat servings (wings, legs, thighs)
- A pile of innards

If you're making soup, you might carve the turkey into:
- A pile of meat
- A pile of bones
- A pile of flavorful innards
- A pile of vile, unnameable things that discourage attention

So: For what purpose are you carving? Where do you carve? What are your reasons for carving there? What elements do you put into each piece?

The answers guide your names.

Dale

David Peterson

unread,
Jan 24, 2011, 4:37:38 PM1/24/11
to growing-object-o...@googlegroups.com
OK, I'm getting the feeling your answer is "piles". ;-)

Lance Walton

unread,
Jan 24, 2011, 6:51:38 PM1/24/11
to growing-object-o...@googlegroups.com
That 'pile of unnameable things that discourage attention' is the investment bank enterprise pile.

Dale Emery

unread,
Jan 24, 2011, 7:26:48 PM1/24/11
to growing-object-o...@googlegroups.com
Hi David,

> OK, I'm getting the feeling your answer is "piles". ;-)

Yes, when the app I'm writing is a turkey. :-P

Dale

David Peterson

unread,
Jan 25, 2011, 3:06:51 AM1/25/11
to growing-object-o...@googlegroups.com

Ah, I prefer goos these days. :-)

David
---
http://blog.davidpeterson.co.uk

Reply all
Reply to author
Forward
0 new messages