|
View:
New views
20 Messages
—
Rating Filter:
Alert me
|
|
|
First Test - Ignoring addListener()Getting started with JMock.. sanity check. (Also, this might be a repeat. I tried to post through the gmane archive, but that didn't appear to work).
I am testing a PresenterFirst pattern.. and want to test with JMock. The Presenter calls the View's addListener method which calls the Presenters run() method, which in turn calls the view creditCardAuthenticated method. I don't care about the listener being called, only that the creditCardAuthenticated method is eventually called. Given all that, is this the right way to construct the test? final IView viewMock = context.mock(IView.class); context.checking(new Expectations() {{ ignoring (any(IView.class)).method("addListener") ; }}) ; context.checking( new Expectations() {{ oneOf (viewMock).creditCardAuthenticated(true); }}); CreditCardPresenter presenter = new CreditCardPresenter(viewMock) ; Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) |
|
|
Re: First Test - Ignoring addListener()Welcome to the club...
This phrase: context.checking(new Expectations() {{ ignoring (any(IView.class)).method("addListener") ; }}) ; isn't right. You ignore the call on a particular view instance, not the class, so it should be: context.checking(new Expectations() {{ ignoring (viewMock).addListener(with(any(<ListenerType>.class))); }}; You can then merge this with the other expectation: context.checking(new Expectations() {{ ignoring (viewMock).addListener(with(any(<ListenerType>.class))); oneOf (viewMock).creditCardAuthenticated(true); }}) ; S On 9 Apr 2009, at 17:43, Greg Akins wrote: > Getting started with JMock.. sanity check. (Also, this might be a > repeat. I tried to post through the gmane archive, but that didn't > appear to work). > > I am testing a PresenterFirst pattern.. and want to test with > JMock. The Presenter calls the View's addListener method which > calls the Presenters run() method, which in turn calls the view > creditCardAuthenticated method. I don't care about the listener > being called, only that the creditCardAuthenticated method is > eventually called. Given all that, is this the right way to > construct the test? > > final IView viewMock = context.mock(IView.class); > > context.checking(new Expectations() {{ > ignoring (any(IView.class)).method("addListener") ; > }}) ; > > context.checking( new Expectations() > {{ > oneOf (viewMock).creditCardAuthenticated(true); > }}); > > CreditCardPresenter presenter = new > CreditCardPresenter(viewMock) ; > > Greg Akins > Director of Software Development > 724.935.8281 x 210 (office) > 724.935.8283 (fax) > 724.454.7790 (cell) > http://www.towercare.com > gakins@... > Steve Freeman Winner of the Agile Alliance Gordon Pask award 2006 http://www.m3p.co.uk M3P Limited. Registered office. 2 Church Street, Burnham, Bucks, SL1 7HZ. Company registered in England & Wales. Number 03689627 --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
RE: First Test - Ignoring addListener()Hi,
Can you provide code snippets of the class under test? If you are mocking addListener() call which should eventually trigger a call to creditCardAuthenticated method then I don't see how you can succeed your test. addListener() will be called and the mock will ignore the call ending your test there and will probably throw an exception because creditCardAuthenticated expectation wasn't met. Maybe you need to change your perspective and create separate tests. One that tests your view to ensure addListener() does call run on the Presenter. Another test for your presenter to ensure run() calls creditCardAuthenticated() method. Having small tests focusing on testing a single thing is better because it makes your tests less brittle to changes. -Guillaume ________________________________ From: Greg Akins [mailto:gakins@...] Sent: Thu 4/9/2009 11:43 AM To: user@... Subject: [jmock-user] First Test - Ignoring addListener() Getting started with JMock.. sanity check. (Also, this might be a repeat. I tried to post through the gmane archive, but that didn't appear to work). I am testing a PresenterFirst pattern.. and want to test with JMock. The Presenter calls the View's addListener method which calls the Presenters run() method, which in turn calls the view creditCardAuthenticated method. I don't care about the listener being called, only that the creditCardAuthenticated method is eventually called. Given all that, is this the right way to construct the test? final IView viewMock = context.mock(IView.class); context.checking(new Expectations() {{ ignoring (any(IView.class)).method("addListener") ; }}) ; context.checking( new Expectations() {{ oneOf (viewMock).creditCardAuthenticated(true); }}); CreditCardPresenter presenter = new CreditCardPresenter(viewMock) ; Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com <http://www.towercare.com/> gakins@... ____________________________________________________________________________________________________ This electronic mail (including any attachments) may contain information that is privileged, confidential, and/or otherwise protected from disclosure to anyone other than its intended recipient(s). Any dissemination or use of this electronic email or its contents (including any attachments) by persons other than the intended recipient(s) is strictly prohibited. If you have received this message in error, please notify us immediately by reply email so that we may correct our internal records. Please then delete the original message (including any attachments) in its entirety. Thank you. --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()Thanks Steve.
So I started by using some examples based on JMock 1 (http://www.atomicobject.com/pages/Presenter+First ). That example used a constraint to capture the listener and execute it. It worked, but I had a brief exchange with Nat on the Yahoo TDD group, and he suggested that the test wasn't well written. That and the fact that it was still in JMock 1 led me to rewrite the test. My class, the JMock 2 based test and the JMock 1 test that I used as a starting point are at http://pastie.org/441932 My test might need refactoring.. I want to test that the presenter calls the method as instructed by the view listener. And that the correct values is assigned, on the view, as a result of the presenters behavior. On Apr 9, 2009, at 11:57 AM, Steve Freeman wrote: > context.checking(new Expectations() {{ > ignoring (viewMock).addListener(with(any(<ListenerType>.class))); > }}; Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com gakins@... --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()On Apr 9, 2009, at 12:03 PM, Jeudy, Guillaume wrote: > Hi, > > Can you provide code snippets of the class under test? If you are > mocking addListener() call which should eventually trigger a call to > creditCardAuthenticated method then I don't see how you can succeed > your test. addListener() will be called and the mock will ignore the > call ending your test there and will probably throw an exception > because creditCardAuthenticated expectation wasn't met. That's exactly what happens. I had, as another post on this thread mentions, seen examples with JMock 1.2 that captured the results of the addListener call and created a proxy to the method which could be executed (at least I think that's how it works). > > Maybe you need to change your perspective and create separate tests. > One that tests your view to ensure addListener() does call run on > the Presenter. Another test for your presenter to ensure run() calls > creditCardAuthenticated() method. Splitting it up like that makes sense. But what I think I need to test here is that if the presenter gives a view a listener and the listener is invoked that it executes the method as expected. Also, run() is private, because it's only called through the listener.. if I can get a proxy to the listener then I don't have to make run() public. This all might be way to complicated, but my motivation is to use PresenterFirst to separate logic from UI and I can't think of another good way to test that. > > Having small tests focusing on testing a single thing is better > because it makes your tests less brittle to changes. > > -Guillaume > > ________________________________ > > From: Greg Akins [mailto:gakins@...] > Sent: Thu 4/9/2009 11:43 AM > To: user@... > Subject: [jmock-user] First Test - Ignoring addListener() > > > Getting started with JMock.. sanity check. (Also, this might be a > repeat. I tried to post through the gmane archive, but that didn't > appear to work). > > I am testing a PresenterFirst pattern.. and want to test with > JMock. The Presenter calls the View's addListener method which > calls the Presenters run() method, which in turn calls the view > creditCardAuthenticated method. I don't care about the listener > being called, only that the creditCardAuthenticated method is > eventually called. Given all that, is this the right way to > construct the test? > > final IView viewMock = context.mock(IView.class); > > context.checking(new Expectations() {{ > ignoring (any(IView.class)).method("addListener") ; > }}) ; > > > > > context.checking( new Expectations() > {{ > oneOf (viewMock).creditCardAuthenticated(true); > }}); > > > > > CreditCardPresenter presenter = new > CreditCardPresenter(viewMock) ; > > Greg Akins > Director of Software Development > 724.935.8281 x 210 (office) > 724.935.8283 (fax) > 724.454.7790 (cell) > http://www.towercare.com <http://www.towercare.com/> > gakins@... > > > > ____________________________________________________________________________________________________ > This electronic mail (including any attachments) may contain > information that is privileged, confidential, and/or otherwise > protected from disclosure to anyone other than its intended > recipient(s). Any dissemination or use of this electronic email or > its contents (including any attachments) by persons other than the > intended recipient(s) is strictly prohibited. If you have received > this message in error, please notify us immediately by reply email > so that we may correct our internal records. Please then delete the > original message (including any attachments) in its entirety. Thank > you. > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com gakins@... --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
RE: First Test - Ignoring addListener()Hi,
I don't know how you setup your tests but if you have a separate folder containing test classes with exactly the same package structure as your production classes you should be able to make you run() method package-private or protected. That way you don't give on too much encapsulation and you still allow your PresenterTest class to see the Presenter run method. ________________________________ From: Greg Akins [mailto:gakins@...] Sent: Thu 4/9/2009 12:27 PM To: user@... Subject: Re: [jmock-user] First Test - Ignoring addListener() On Apr 9, 2009, at 12:03 PM, Jeudy, Guillaume wrote: > Hi, > > Can you provide code snippets of the class under test? If you are > mocking addListener() call which should eventually trigger a call to > creditCardAuthenticated method then I don't see how you can succeed > your test. addListener() will be called and the mock will ignore the > call ending your test there and will probably throw an exception > because creditCardAuthenticated expectation wasn't met. That's exactly what happens. I had, as another post on this thread mentions, seen examples with JMock 1.2 that captured the results of the addListener call and created a proxy to the method which could be executed (at least I think that's how it works). > > Maybe you need to change your perspective and create separate tests. > One that tests your view to ensure addListener() does call run on > the Presenter. Another test for your presenter to ensure run() calls > creditCardAuthenticated() method. Splitting it up like that makes sense. But what I think I need to test here is that if the presenter gives a view a listener and the listener is invoked that it executes the method as expected. Also, run() is private, because it's only called through the listener.. if I can get a proxy to the listener then I don't have to make run() public. This all might be way to complicated, but my motivation is to use PresenterFirst to separate logic from UI and I can't think of another good way to test that. > > Having small tests focusing on testing a single thing is better > because it makes your tests less brittle to changes. > > -Guillaume > > ________________________________ > > From: Greg Akins [mailto:gakins@...] > Sent: Thu 4/9/2009 11:43 AM > To: user@... > Subject: [jmock-user] First Test - Ignoring addListener() > > > Getting started with JMock.. sanity check. (Also, this might be a > repeat. I tried to post through the gmane archive, but that didn't > appear to work). > > I am testing a PresenterFirst pattern.. and want to test with > JMock. The Presenter calls the View's addListener method which > calls the Presenters run() method, which in turn calls the view > creditCardAuthenticated method. I don't care about the listener > being called, only that the creditCardAuthenticated method is > eventually called. Given all that, is this the right way to > construct the test? > > final IView viewMock = context.mock(IView.class); > > context.checking(new Expectations() {{ > ignoring (any(IView.class)).method("addListener") ; > }}) ; > > > > > context.checking( new Expectations() > {{ > oneOf (viewMock).creditCardAuthenticated(true); > }}); > > > > > CreditCardPresenter presenter = new > CreditCardPresenter(viewMock) ; > > Greg Akins > Director of Software Development > 724.935.8281 x 210 (office) > 724.935.8283 (fax) > 724.454.7790 (cell) > http://www.towercare.com <http://www.towercare.com/> <http://www.towercare.com/> > gakins@... > > > > ____________________________________________________________________________________________________ > This electronic mail (including any attachments) may contain > information that is privileged, confidential, and/or otherwise > protected from disclosure to anyone other than its intended > recipient(s). Any dissemination or use of this electronic email or > its contents (including any attachments) by persons other than the > intended recipient(s) is strictly prohibited. If you have received > this message in error, please notify us immediately by reply email > so that we may correct our internal records. Please then delete the > original message (including any attachments) in its entirety. Thank > you. > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com <http://www.towercare.com/> gakins@... --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email ____________________________________________________________________________________________________ This electronic mail (including any attachments) may contain information that is privileged, confidential, and/or otherwise protected from disclosure to anyone other than its intended recipient(s). Any dissemination or use of this electronic email or its contents (including any attachments) by persons other than the intended recipient(s) is strictly prohibited. If you have received this message in error, please notify us immediately by reply email so that we may correct our internal records. Please then delete the original message (including any attachments) in its entirety. Thank you. --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()2009/4/9 Jeudy, Guillaume <gjeudy@...>:
> Hi, > > I don't know how you setup your tests but if you have a separate folder containing test classes with exactly the same package structure as your production classes you should be able to make you run() method package-private or protected. That way you don't give on too much encapsulation and you still allow your PresenterTest class to see the Presenter run method. > But then the tests aren't exercising the object in the same way as it's used in the production code, so there's a risk of a defect slipping in, and of tests diverging from the code when either are changed. That's why I prefer to put tests in a different package from the production code: they are forced to exercise the object through its API without reaching in through some backdoor. And, if that's difficult, it pushes me to sort out the design problems that are making it difficult. In this case, I'd implement an Action that held on to the listener and let you call back to it from the test. For a simple example, see http://www.jmock.org/gwt.html. --Nat --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
RE: First Test - Ignoring addListener()Thanks for your input Nat,
However it's not clear to me how Greg can apply your technique to his usecase. Giving it a try, so basically we would call Presenter.addListener() pass to it a mock that has an expectation set similar to: public class RunAction<T> implements Action { private ViewListener<T> listener; public void succeedGiving(T result) { checkCallback(); listener.run(); } ...<snip><snip> private void checkCallback() { if(callback == null) { Assert.fail("run action not scheduled"); } } public Object invoke(Invocation invocation) throws Throwable { listener = (ViewListener<T>)invocation.getParameter(invocation.getParameterCount()-1); return null; } } context.checking(new Expectations() {{ oneOf(mockView).addListener(with(any(IPresenter.class))); will(callRunMethod); }}); The above might work if run() method is exposed through the listener interface. Greg is that possible? Without seeing your code it's hard to work on a solution. Nat, let me know if i'm off the tracks here. ________________________________ From: Nat Pryce [mailto:nat.pryce@...] Sent: Thu 4/9/2009 2:12 PM To: user@... Subject: Re: [jmock-user] First Test - Ignoring addListener() 2009/4/9 Jeudy, Guillaume <gjeudy@...>: > Hi, > > I don't know how you setup your tests but if you have a separate folder containing test classes with exactly the same package structure as your production classes you should be able to make you run() method package-private or protected. That way you don't give on too much encapsulation and you still allow your PresenterTest class to see the Presenter run method. > But then the tests aren't exercising the object in the same way as it's used in the production code, so there's a risk of a defect slipping in, and of tests diverging from the code when either are changed. That's why I prefer to put tests in a different package from the production code: they are forced to exercise the object through its API without reaching in through some backdoor. And, if that's difficult, it pushes me to sort out the design problems that are making it difficult. In this case, I'd implement an Action that held on to the listener and let you call back to it from the test. For a simple example, see http://www.jmock.org/gwt.html. --Nat --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email ____________________________________________________________________________________________________ This electronic mail (including any attachments) may contain information that is privileged, confidential, and/or otherwise protected from disclosure to anyone other than its intended recipient(s). Any dissemination or use of this electronic email or its contents (including any attachments) by persons other than the intended recipient(s) is strictly prohibited. If you have received this message in error, please notify us immediately by reply email so that we may correct our internal records. Please then delete the original message (including any attachments) in its entirety. Thank you. --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()2009/4/9 Nat Pryce <nat.pryce@...>:
> 2009/4/9 Jeudy, Guillaume <gjeudy@...>: >> Hi, >> >> I don't know how you setup your tests but if you have a separate folder containing test classes with exactly the same package structure as your production classes you should be able to make you run() method package-private or protected. That way you don't give on too much encapsulation and you still allow your PresenterTest class to see the Presenter run method. >> > > But then the tests aren't exercising the object in the same way as > it's used in the production code, so there's a risk of a defect > slipping in, and of tests diverging from the code when either are > changed. That's why I prefer to put tests in a different package from > the production code: they are forced to exercise the object through > its API without reaching in through some backdoor. And, if that's > difficult, it pushes me to sort out the design problems that are > making it difficult. > > In this case, I'd implement an Action that held on to the listener and > let you call back to it from the test. For a simple example, see > http://www.jmock.org/gwt.html. I should have added, the awkward design smell here is that the presenter adds *itself* as a listener to the view. I would expect that connection to be established by some third party. There are three peers: the presenter, the model and the view. But they are internal components of something else (maybe the application, or perhaps a subsystem). That thing should be connecting them up: in this case passing the view's control interface to the presenter's constructor and registering the presenter to receive notifications from the view. Then the whole issue of ignoring the addListener call goes away. The unit test demonstrates how the presenter behaves if it receives view notifications, and it's up to the user of the presenter to ensure that it does receive them. --Nat -- http://www.natpryce.com --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
RE: First Test - Ignoring addListener()Maybe the presenter does not add itself to the view after all. I see how the run() method can be called.
Greg probably creates an anonymous listener class inside the Presenter, passes it to View.addListener() and can call private run() inline from the listener code. I found that in the following example: http://www.atomicobject.com/files/PresenterFirstExample.zip But your last comment makes alot of sense anyways. Then no need to mess with addListener(), once Presenter and View are wired by a 3rd party class, presenter can then be tested by calling the view event generating method. ________________________________ From: Nat Pryce [mailto:nat.pryce@...] Sent: Thu 4/9/2009 2:42 PM To: user@... Subject: Re: [jmock-user] First Test - Ignoring addListener() 2009/4/9 Nat Pryce <nat.pryce@...>: > 2009/4/9 Jeudy, Guillaume <gjeudy@...>: >> Hi, >> >> I don't know how you setup your tests but if you have a separate folder containing test classes with exactly the same package structure as your production classes you should be able to make you run() method package-private or protected. That way you don't give on too much encapsulation and you still allow your PresenterTest class to see the Presenter run method. >> > > But then the tests aren't exercising the object in the same way as > it's used in the production code, so there's a risk of a defect > slipping in, and of tests diverging from the code when either are > changed. That's why I prefer to put tests in a different package from > the production code: they are forced to exercise the object through > its API without reaching in through some backdoor. And, if that's > difficult, it pushes me to sort out the design problems that are > making it difficult. > > In this case, I'd implement an Action that held on to the listener and > let you call back to it from the test. For a simple example, see > http://www.jmock.org/gwt.html. presenter adds *itself* as a listener to the view. I would expect that connection to be established by some third party. There are three peers: the presenter, the model and the view. But they are internal components of something else (maybe the application, or perhaps a subsystem). That thing should be connecting them up: in this case passing the view's control interface to the presenter's constructor and registering the presenter to receive notifications from the view. Then the whole issue of ignoring the addListener call goes away. The unit test demonstrates how the presenter behaves if it receives view notifications, and it's up to the user of the presenter to ensure that it does receive them. --Nat -- http://www.natpryce.com <http://www.natpryce.com/> --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email ____________________________________________________________________________________________________ This electronic mail (including any attachments) may contain information that is privileged, confidential, and/or otherwise protected from disclosure to anyone other than its intended recipient(s). Any dissemination or use of this electronic email or its contents (including any attachments) by persons other than the intended recipient(s) is strictly prohibited. If you have received this message in error, please notify us immediately by reply email so that we may correct our internal records. Please then delete the original message (including any attachments) in its entirety. Thank you. --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()2009/4/9 Jeudy, Guillaume <gjeudy@...>:
> Thanks for your input Nat, > > However it's not clear to me how Greg can apply your technique to his usecase. > ... > > Nat, let me know if i'm off the tracks here. Yes, you've got the relationships the wrong way round I think. The presenter calls addListener on the view, passing to the view some kind of listener interface that the view will call back to. We're mocking the view, so our test will have to call back to that interface. We will have to grab hold of it when the presenter calls our mocked addListener interface. We can do that with a custom Action. Then, to invoke whatever the presenter does in response to view events, we will call method(s) of the listener interface captured by our custom action. We then don't care how the presenter handles the notification from the view, as long as it produces the *externally visible* behaviour we want (that is, results in a call to the view's creditCardAuthenticated method). The fact that the presenter has a run() method and whatnot is an implementation detail that our test should not be concerned with. I'd imagine a custom action for this would look something like: public class AddListenerAction<T extends EventListener> { private T listener = null; @SuppressWarnings("unchecked") public void invoke(Invocation i) { listener = (T)i.getParameter(0); } public T notify() { if (listener == null) Assert.fail("no listener has been added"); return listener; } } And a test would look something like: final AddListenerAction<ViewListener> registerListener = new AddListenerAction<ViewListener>(); context.checking(new Expectations() {{ oneOf(view).addListener(with(any(ViewListener.class)); will(registerListener); oneOf (viewMock).creditCardAuthenticated(true); }}); Presenter presenter = new Presenter(view); // this adds the listener registerListener.notify().someViewEventHere(); // this calls it. } --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
RE: First Test - Ignoring addListener()Ok, thanks Nat that clears it up for me.
However I think the unit test might be easier to understand if one followed the advice you gave which is not to have the Presenter wire itself to the View. A 3rd party object (some kind of factory) could do that job; then no need to capture the listener in an Action, just call the view event listener interface directly and observe the presenter's indirect outputs (creditCardAuthenticated call). ________________________________ From: Nat Pryce [mailto:nat.pryce@...] Sent: Thu 4/9/2009 2:54 PM To: user@... Subject: Re: [jmock-user] First Test - Ignoring addListener() 2009/4/9 Jeudy, Guillaume <gjeudy@...>: > Thanks for your input Nat, > > However it's not clear to me how Greg can apply your technique to his usecase. > ... > > Nat, let me know if i'm off the tracks here. Yes, you've got the relationships the wrong way round I think. The presenter calls addListener on the view, passing to the view some kind of listener interface that the view will call back to. We're mocking the view, so our test will have to call back to that interface. We will have to grab hold of it when the presenter calls our mocked addListener interface. We can do that with a custom Action. Then, to invoke whatever the presenter does in response to view events, we will call method(s) of the listener interface captured by our custom action. We then don't care how the presenter handles the notification from the view, as long as it produces the *externally visible* behaviour we want (that is, results in a call to the view's creditCardAuthenticated method). The fact that the presenter has a run() method and whatnot is an implementation detail that our test should not be concerned with. I'd imagine a custom action for this would look something like: public class AddListenerAction<T extends EventListener> { private T listener = null; @SuppressWarnings("unchecked") public void invoke(Invocation i) { listener = (T)i.getParameter(0); } public T notify() { if (listener == null) Assert.fail("no listener has been added"); return listener; } } And a test would look something like: final AddListenerAction<ViewListener> registerListener = new AddListenerAction<ViewListener>(); context.checking(new Expectations() {{ oneOf(view).addListener(with(any(ViewListener.class)); will(registerListener); oneOf (viewMock).creditCardAuthenticated(true); }}); Presenter presenter = new Presenter(view); // this adds the listener registerListener.notify().someViewEventHere(); // this calls it. } --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email ____________________________________________________________________________________________________ This electronic mail (including any attachments) may contain information that is privileged, confidential, and/or otherwise protected from disclosure to anyone other than its intended recipient(s). Any dissemination or use of this electronic email or its contents (including any attachments) by persons other than the intended recipient(s) is strictly prohibited. If you have received this message in error, please notify us immediately by reply email so that we may correct our internal records. Please then delete the original message (including any attachments) in its entirety. Thank you. --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()Jeudy & Nat.. thanks so much for this discussion. I had to step away
for a few hours, so I am catching up. My goal is to have less dependency on the UI code. I'm working in a legacy application where much of the business logic is in Swing components. PresenterFirst seemed to offer some hope. I haven't read your comments enough write any testable code, but I the PF model seemed like just the ticket for allowing most of the behavior to be in a class that was agnostic of the UI code and could be tested. On Apr 9, 2009, at 3:05 PM, Jeudy, Guillaume wrote: > Ok, thanks Nat that clears it up for me. > > However I think the unit test might be easier to understand if one > followed the advice you gave which is not to have the Presenter wire > itself to the View. A 3rd party object (some kind of factory) could > do that job; then no need to capture the listener in an Action, just > call the view event listener interface directly and observe the > presenter's indirect outputs (creditCardAuthenticated call). > > ________________________________ > > From: Nat Pryce [mailto:nat.pryce@...] > Sent: Thu 4/9/2009 2:54 PM > To: user@... > Subject: Re: [jmock-user] First Test - Ignoring addListener() > > > > 2009/4/9 Jeudy, Guillaume <gjeudy@...>: >> Thanks for your input Nat, >> >> However it's not clear to me how Greg can apply your technique to >> his usecase. >> > ... >> >> Nat, let me know if i'm off the tracks here. > > Yes, you've got the relationships the wrong way round I think. > > The presenter calls addListener on the view, passing to the view some > kind of listener interface that the view will call back to. > > We're mocking the view, so our test will have to call back to that > interface. We will have to grab hold of it when the presenter calls > our mocked addListener interface. We can do that with a custom > Action. > > Then, to invoke whatever the presenter does in response to view > events, we will call method(s) of the listener interface captured by > our custom action. > > We then don't care how the presenter handles the notification from the > view, as long as it produces the *externally visible* behaviour we > want (that is, results in a call to the view's creditCardAuthenticated > method). The fact that the presenter has a run() method and whatnot > is an implementation detail that our test should not be concerned > with. > > I'd imagine a custom action for this would look something like: > > public class AddListenerAction<T extends EventListener> { > private T listener = null; > > > @SuppressWarnings("unchecked") > public void invoke(Invocation i) { > listener = (T)i.getParameter(0); > } > > public T notify() { > if (listener == null) Assert.fail("no listener has been > added"); > return listener; > } > } > > And a test would look something like: > > > final AddListenerAction<ViewListener> registerListener = > new AddListenerAction<ViewListener>(); > > context.checking(new Expectations() {{ > oneOf(view).addListener(with(any(ViewListener.class)); > will(registerListener); > > oneOf (viewMock).creditCardAuthenticated(true); > }}); > > Presenter presenter = new Presenter(view); // this adds the listener > registerListener.notify().someViewEventHere(); // this calls it. > } > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > > > > > > > ____________________________________________________________________________________________________ > This electronic mail (including any attachments) may contain > information that is privileged, confidential, and/or otherwise > protected from disclosure to anyone other than its intended > recipient(s). Any dissemination or use of this electronic email or > its contents (including any attachments) by persons other than the > intended recipient(s) is strictly prohibited. If you have received > this message in error, please notify us immediately by reply email > so that we may correct our internal records. Please then delete the > original message (including any attachments) in its entirety. Thank > you. > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com gakins@... --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
RE: First Test - Ignoring addListener()Greg,
I agree Presenter First offers some hope but that will require you to move out alot of the existing logic from your view (swing component) into your presenter object. It depends on your tolerance to risk, some people would advocate writing blackbox integration tests first for the existing components (that might prove challenging) and then use that as a safety net while you conduct your refactorings to allow better automated unit testing. ________________________________ From: Greg Akins [mailto:gakins@...] Sent: Thu 4/9/2009 3:19 PM To: user@... Subject: Re: [jmock-user] First Test - Ignoring addListener() Jeudy & Nat.. thanks so much for this discussion. I had to step away for a few hours, so I am catching up. My goal is to have less dependency on the UI code. I'm working in a legacy application where much of the business logic is in Swing components. PresenterFirst seemed to offer some hope. I haven't read your comments enough write any testable code, but I the PF model seemed like just the ticket for allowing most of the behavior to be in a class that was agnostic of the UI code and could be tested. On Apr 9, 2009, at 3:05 PM, Jeudy, Guillaume wrote: > Ok, thanks Nat that clears it up for me. > > However I think the unit test might be easier to understand if one > followed the advice you gave which is not to have the Presenter wire > itself to the View. A 3rd party object (some kind of factory) could > do that job; then no need to capture the listener in an Action, just > call the view event listener interface directly and observe the > presenter's indirect outputs (creditCardAuthenticated call). > > ________________________________ > > From: Nat Pryce [mailto:nat.pryce@...] > Sent: Thu 4/9/2009 2:54 PM > To: user@... > Subject: Re: [jmock-user] First Test - Ignoring addListener() > > > > 2009/4/9 Jeudy, Guillaume <gjeudy@...>: >> Thanks for your input Nat, >> >> However it's not clear to me how Greg can apply your technique to >> his usecase. >> > ... >> >> Nat, let me know if i'm off the tracks here. > > Yes, you've got the relationships the wrong way round I think. > > The presenter calls addListener on the view, passing to the view some > kind of listener interface that the view will call back to. > > We're mocking the view, so our test will have to call back to that > interface. We will have to grab hold of it when the presenter calls > our mocked addListener interface. We can do that with a custom > Action. > > Then, to invoke whatever the presenter does in response to view > events, we will call method(s) of the listener interface captured by > our custom action. > > We then don't care how the presenter handles the notification from the > view, as long as it produces the *externally visible* behaviour we > want (that is, results in a call to the view's creditCardAuthenticated > method). The fact that the presenter has a run() method and whatnot > is an implementation detail that our test should not be concerned > with. > > I'd imagine a custom action for this would look something like: > > public class AddListenerAction<T extends EventListener> { > private T listener = null; > > > @SuppressWarnings("unchecked") > public void invoke(Invocation i) { > listener = (T)i.getParameter(0); > } > > public T notify() { > if (listener == null) Assert.fail("no listener has been > added"); > return listener; > } > } > > And a test would look something like: > > > final AddListenerAction<ViewListener> registerListener = > new AddListenerAction<ViewListener>(); > > context.checking(new Expectations() {{ > oneOf(view).addListener(with(any(ViewListener.class)); > will(registerListener); > > oneOf (viewMock).creditCardAuthenticated(true); > }}); > > Presenter presenter = new Presenter(view); // this adds the listener > registerListener.notify().someViewEventHere(); // this calls it. > } > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > > > > > > > ____________________________________________________________________________________________________ > This electronic mail (including any attachments) may contain > information that is privileged, confidential, and/or otherwise > protected from disclosure to anyone other than its intended > recipient(s). Any dissemination or use of this electronic email or > its contents (including any attachments) by persons other than the > intended recipient(s) is strictly prohibited. If you have received > this message in error, please notify us immediately by reply email > so that we may correct our internal records. Please then delete the > original message (including any attachments) in its entirety. Thank > you. > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com <http://www.towercare.com/> gakins@... --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email ____________________________________________________________________________________________________ This electronic mail (including any attachments) may contain information that is privileged, confidential, and/or otherwise protected from disclosure to anyone other than its intended recipient(s). Any dissemination or use of this electronic email or its contents (including any attachments) by persons other than the intended recipient(s) is strictly prohibited. If you have received this message in error, please notify us immediately by reply email so that we may correct our internal records. Please then delete the original message (including any attachments) in its entirety. Thank you. --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()On Apr 9, 2009, at 2:42 PM, Nat Pryce wrote: > I should have added, the awkward design smell here is that the > presenter adds *itself* as a listener to the view. I would expect > that connection to be established by some third party. So the third party is the "Application". But in test, the thirdparty is the JUnit test, right? So Presenter has a View and a Model. The Application gives the View to the Presenter. > > There are three peers: the presenter, the model and the view. But > they are internal components of something else (maybe the application, > or perhaps a subsystem). That thing should be connecting them up: in > this case passing the view's control interface to the presenter's > constructor and registering the presenter to receive notifications > from the view. I'm going to try this, though I can't see how I register the presenter to receive notification from the view any differently than hooking up the listeners like I am now. I have to admit that I'm not a very talented Swing developer and don't always understand well how the Listeners work. > > Then the whole issue of ignoring the addListener call goes away. The > unit test demonstrates how the presenter behaves if it receives view > notifications, and it's up to the user of the presenter to ensure that > it does receive them. > > --Nat > > -- > http://www.natpryce.com > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com gakins@... --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()On Apr 9, 2009, at 3:05 PM, Jeudy, Guillaume wrote:
>just call the view event listener interface I'm drawing a blank, probably from staring at this so much already. How do I call ActionPerformed on a listener? Are you saying I should simple add a getListener() to my View? I started writing something like the below code. But then it occurred to me that you're suggesting that I just call View.getListener().ActionPerformed() and then Presenter.creditCardAuthenticated() I'm sorry about this.. I feel more stupid the longer I try to get it working. public void testActivateListener() { final IView viewMock = context.mock(IView.class); CreditCardPresenter presenter = new CreditCardPresenter(viewMock); context.checking(new Expectations() { { ignoring(viewMock).addListener(with(any(ActionListener.class))); } }); //Now I want to make sure that anything that happens // to the View is known about in the Presenter viewMock.addListener(presenter.getListener()) ; //that passes because I'm ignoring addListener calls.. //Now I want to find out if "activating" the listener results in // the Presenter method being invoked. //In JMock 1.2 syntax, because I can't figure out how to do // the same thing in JMock 2.5 ((ActionListener)keepObjectConstraint.getEvalObject()).actionPerformed(null); }
Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) |
|
|
RE: First Test - Ignoring addListener()Let me give this a try:
Note this test is solely focusing on testing the Presenter and not the wiring between the View and the Presenter that happens in the real application.
public void testActivateListener()
{
final IView viewMock = context.mock(IView.class);
CreditCardPresenter presenter = new CreditCardPresenter(viewMock);
context.checking(new Expectations()
{
{
oneOf (viewMock).creditCardAuthenticated(true);
} });
// Fake the view triggering the listener
presenter.getListener().actionPerformed();
} Alternatively and if you want to test the wiring between View and Presenter you could extract the Listener implementation from the Presenter and perform wiring in your tests. But that would require your Listener to have access to the Presenter API and would require you to use the real View implementation. Not if that would be possible.
public class AuthenticateCreditCardListener implements Listener { private IPresenter presenter;
public AuthenticateCreditCardListener (IPresenter presenter) {
this.presenter = presenter;
}
public void actionPerformed() {
this.presenter.handleCreditCardAuthentication();
}
}
public void testActivateListener()
{
IView view = new SwingViewImpl();
CreditCardPresenter presenter = new CreditCardPresenter(view);
Listener listener = new AuthenticateCreditCardListener(presenter );
view.addListener(listener); // how to verify expectations ? maybe mock Presenter and verify view is calling listener which in turns calls the presenter? } Please note that Nat's solution using a custom Action would work as well and has the benefit of testing the Presenter register it's listener to the view. From: Greg Akins [mailto:gakins@...] Sent: Thu 4/9/2009 4:47 PM To: user@... Subject: Re: [jmock-user] First Test - Ignoring addListener() On Apr 9, 2009, at 3:05 PM, Jeudy, Guillaume wrote:
>just call the view event listener interface
I'm drawing a blank, probably from staring at this so much already. How do I call ActionPerformed on a listener? Are you saying I should simple add a getListener() to my View?
I started writing something like the below code. But then it occurred to me that you're suggesting that I just call View.getListener().ActionPerformed() and then Presenter.creditCardAuthenticated()
I'm sorry about this.. I feel more stupid the longer I try to get it working.
public void testActivateListener()
{
final IView viewMock = context.mock(IView.class);
CreditCardPresenter presenter = new CreditCardPresenter(viewMock);
context.checking(new Expectations()
{
{
ignoring(viewMock).addListener(with(any(ActionListener.class)));
}
});
//Now I want to make sure that anything that happens
// to the View is known about in the Presenter
viewMock.addListener(presenter.getListener()) ;
//that passes because I'm ignoring addListener calls..
//Now I want to find out if "activating" the listener results in
// the Presenter method being invoked.
//In JMock 1.2 syntax, because I can't figure out how to do
// the same thing in JMock 2.5
((ActionListener)keepObjectConstraint.getEvalObject()).actionPerformed(null);
}
Greg Akins
Director of Software Development
724.935.8281 x 210 (office)
724.935.8283 (fax)
724.454.7790 (cell)
This electronic mail (including any attachments) may contain information that is privileged, confidential, and/or otherwise protected from disclosure to anyone other than its intended recipient(s). Any dissemination or use of this electronic email or its contents (including any attachments) by persons other than the intended recipient(s) is strictly prohibited. If you have received this message in error, please notify us immediately by reply email so that we may correct our internal records. Please then delete the original message (including any attachments) in its entirety. Thank you. |
|
|
Re: First Test - Ignoring addListener()Thanks for both examples, I'm going to try to work through both of them.
In Nat's, I'm unsure what Invocation Type is being used. This is in the production, not test, code. So it shouldn't be a JMock invocation. >> public class AddListenerAction<T extends EventListener> { >> private T listener = null; >> >> >> @SuppressWarnings("unchecked") >> public void invoke(Invocation i) { >> listener = (T)i.getParameter(0); >> } >> >> public T notify() { >> if (listener == null) Assert.fail("no listener has been >> added"); >> return listener; >> } >> } Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com gakins@... --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()This is test code. It's an implementation of jMock's Action interface.
Check out the jMock cookbook for more examples, and the jMock javadoc for documentation about the Action and Invocation types. --Nat 2009/4/10 Greg Akins <gakins@...>: > Thanks for both examples, I'm going to try to work through both of them. > > In Nat's, I'm unsure what Invocation Type is being used. This is in the > production, not test, code. So it shouldn't be a JMock invocation. > > >>> public class AddListenerAction<T extends EventListener> { >>> private T listener = null; >>> >>> >>> @SuppressWarnings("unchecked") >>> public void invoke(Invocation i) { >>> listener = (T)i.getParameter(0); >>> } >>> >>> public T notify() { >>> if (listener == null) Assert.fail("no listener has been added"); >>> return listener; >>> } >>> } > > Greg Akins > Director of Software Development > 724.935.8281 x 210 (office) > 724.935.8283 (fax) > 724.454.7790 (cell) > http://www.towercare.com > gakins@... > > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > > -- http://www.natpryce.com --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: First Test - Ignoring addListener()Thanks.. I misunderstood the example.
On Apr 9, 2009, at 8:25 PM, Nat Pryce wrote: > This is test code. It's an implementation of jMock's Action > interface. > > Check out the jMock cookbook for more examples, and the jMock javadoc > for documentation about the Action and Invocation types. > > --Nat > > 2009/4/10 Greg Akins <gakins@...>: >> Thanks for both examples, I'm going to try to work through both of >> them. >> >> In Nat's, I'm unsure what Invocation Type is being used. This is >> in the >> production, not test, code. So it shouldn't be a JMock invocation. >> >> >>>> public class AddListenerAction<T extends EventListener> { >>>> private T listener = null; >>>> >>>> >>>> @SuppressWarnings("unchecked") >>>> public void invoke(Invocation i) { >>>> listener = (T)i.getParameter(0); >>>> } >>>> >>>> public T notify() { >>>> if (listener == null) Assert.fail("no listener has been >>>> added"); >>>> return listener; >>>> } >>>> } >> >> Greg Akins >> Director of Software Development >> 724.935.8281 x 210 (office) >> 724.935.8283 (fax) >> 724.454.7790 (cell) >> http://www.towercare.com >> gakins@... >> >> >> --------------------------------------------------------------------- >> To unsubscribe from this list, please visit: >> >> http://xircles.codehaus.org/manage_email >> >> >> > > > > -- > http://www.natpryce.com > > --------------------------------------------------------------------- > To unsubscribe from this list, please visit: > > http://xircles.codehaus.org/manage_email > > Greg Akins Director of Software Development 724.935.8281 x 210 (office) 724.935.8283 (fax) 724.454.7790 (cell) http://www.towercare.com gakins@... --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
| Free embeddable forum powered by Nabble | Forum Help |