The following code snippet will run all listeners on a given ListBox,
programmatically. You'll have to write similar stuff, but make sure
you browse the GWT code for the 'most simple superclass' that has the
'xyzListeners' object. For example, Button, CheckBox, and all other
focusable widgets extend FocusWidget (e.g Button is Button->ButtonBase-
>FocusWidget) and FocusWidget has the clickListeners object, so you'd
write the 'getClickListeners' native method with as parameter the
FocusWidget.
snippet:
public class TriggerListenersUtility {
private static native ChangeListenerCollection
getChangeListeners(ListBox listBox) /*-{
return
listBox.@com.google.gwt.user.client.ui.ListBox::changeListeners;
}-*/;
public static fireAllListeners(ListBox listBox) {
ChangeListenerCollection clc = getChangeListeners(listBox);
if ( clc != null ) clc.fireChange(listBox);
}
}
enjoy!
I have maintained that if you are writing your GWT app in the
canonical manner, much like traditional desktop app toolkits - the
client has a model and controller, and the view is observing the model
- then all you are testing is whether or not the button event works.
Meaning, you are not testing any of your code, you are testing the
toolkit, and once the event happens your code will be invoked, but it
is often easier/cleaner in unit tests to invoke the controller methods
directly, which update the model, and skip the view (come back to that
for integration tests/etc).
Now, if you are not using MVC as such, in the client, and you have a
mess of logic in the UI component, or you have some other design where
the view is otherwise not just a thin veneer on top of the model, then
yeah you might want to try to test it - but you might also be better
served re-design things to avoid that.
I know you are not one to hold back RZ, so I am really quite
interested in your take on this. Should people be trying to test
things to the component UI event handling level, in GWT unit tests?
On Apr 4, 1:31 am, "Reinier Zwitserloot" <reini...@gmail.com> wrote:
> I've seen at least 4 posts over the past couple of months asking how
> to fire listeners programmatically, usually to expediate some unit
> tests. Until now I've always said it wasn't possible. Due to Sandy's
> insights on how JSNI doesn't have all that many restrictions, I was
> wrong, it is possible.
>
> The following code snippet will run all listeners on a given ListBox,
> programmatically. You'll have to write similar stuff, but make sure
> you browse the GWT code for the 'most simple superclass' that has the
> 'xyzListeners' object. For example, Button, CheckBox, and all other
> focusable widgets extend FocusWidget (e.g Button is Button->ButtonBase->FocusWidget) and FocusWidget has the clickListeners object, so you'd
>
> write the 'getClickListeners' native method with as parameter the
> FocusWidget.
>
> snippet:
>
> public class TriggerListenersUtility {
> private static native ChangeListenerCollection
> getChangeListeners(ListBox listBox) /*-{
> return
> listB...@com.google.gwt.user.client.ui.ListBox::changeListeners;
> It is possible, sometimes, and informative, but is it worthwhile?
>
> I have maintained that if you are writing your GWT app in the
> canonical manner, much like traditional desktop app toolkits - the
> client has a model and controller, and the view is observing the model
> - then all you are testing is whether or not the button event works.
> Meaning, you are not testing any of your code, you are testing the
> toolkit, and once the event happens your code will be invoked, but it
> is often easier/cleaner in unit tests to invoke the controller methods
> directly, which update the model, and skip the view (come back to that
> for integration tests/etc).
>
> Now, if you are not using MVC as such, in the client, and you have a
> mess of logic in the UI component, or you have some other design where
> the view is otherwise not just a thin veneer on top of the model, then
> yeah you might want to try to test it - but you might also be better
> served re-design things to avoid that.
>
> I know you are not one to hold back RZ, so I am really quite
> interested in your take on this. Should people be trying to test
> things to the component UI event handling level, in GWT unit tests?
My take on this is that the logic that updates the GUI in an event
handler sometimes benefits from automated tests. The logic there
isn't always simple and so deserves to be protected from being
inadvertently broken by client-side code changes.
Here's an example of something you might not test. When the user
clicks the "Go" button, the ClickListener gets the value of a text
box, passes it to a GWT RPC service, gets back a String, and changes
the text of Label to that String. Pretty straight forward. You don't
need to write a test for this. It is easy to do though.
Here's an example that perhaps deserves a test. When the user clicks
the "Go" button, the ClickListener does a large number of things. It
gets the values of many widgets, populates a Map of Java objects with
their values, passes the Map to a GWT RPC service, gets back a List
of objects, populates a FlexTable with the data in them, enables/
disables many widgets based on the data returned, ...
Probably not likely as if it were to change in an incompatible way
then existing code internal to GWT would need to be changed too.
Inertia will probably prevent that from happening.
But you are right, it's not a public API and if you use it you're on
your own when it breaks. I feel like I just gave Reinier a loaded gun
and he'll be shooting a lot of people in the feet with this technique.
:-(
--
Sandy McArthur
"He who dares not offend cannot be honest."
- Thomas Paine
changeListeners are only called when the change is the result of an
user operation (at least for ListBoxes, which was the original
problem) - however if the content of the ListBox is filled for example
by a callback function, the listener is not called - this is a bit of
a problem because to fix it you need to either make the callback
function to be more aware of the ListBox specifics than it should (if
you want to add additional handling after filling it, that's it).
I think a change is a change, whether the user caused it or something
else.
Now, I can see reasons not to automatically run changeListeners, but
at least it should be possible to do it programatically without doing
funny things.
On Apr 4, 1:59 pm, "charlie.coll...@gmail.com"
"When a user clicks the "Go" button, the ClickListener does" . . .
nothing but call the controller.go() method. Then the controller does
all the RPC
stuff and updates the model, and other view components that are
observing the model
then update.
What I am talking about is exactly NOT doing what you state in that
"perhaps deserves a test" scenario.
Rather move that into a controller, on the client, and leave the UI to
worry about UI?
On Apr 4, 8:49 am, Mark Volkmann <m...@ociweb.com> wrote:
I am willing to learn here, and certainly admit I may be missing
something, but based on these explanations I still do not "get" the
need for that level of UI testing. (And what happens when your
widgets are composite?There may be many scenarios where your UI will
not expose all the event handling it has internally.)
Charlie: Yes and No. If you can avoid using this, by all means, do so.
However, at the end of the day, it's a worthwhile test to do the
following:
A. (fake) pressing some button,
B. check if a bunch of things happend.
WithOUT placing a restriction that the steps that cause A to lead to B
are all stored in a single clickListener that the tester knows about
(or multiple ones, which the tester all knows about, INCLUDING the
order in which they were added!!)
Clearly, to me, there are situations where the above test is more
reasonable than doing the logistics yourself. I've always maintained
that some sort of 'getAllListeners()' method on all widgets with
listeners is a bad move, because you should never add to a public API
methods that are intended only for writing tests, but this sidesteps
that issue nicely.
Mark: Exactly.
abickford, sandy: Yes, it might break. No, as Sandy mentioned, in
practice I don't think this'll happen very often; virtually impossible
in point updates, very unlikely in minor release updates, and maybe
every third major upgrade, if the event handling system gets an
update. As far as I can see, on the books, there are no complaints
about the event system, which makes it rather unlikely.
In the post that lead up to this one (the feature request) I mentioned
that I thought 1 warning per project (not a torrent, that's not
useful, I think) is warranted just to point out that what you're doing
will work fine, but may not be source-compatible with future releases
(in GWT land you don't need to think about binary compatibility. How
nice!)
This suggestion can still stand: Anytime the GWT compiler figures out
you're doing something 'bad' (changing a final, accessing a non-
visible), you get a warning that what you're doing will work, but
should be a last resort, and that future source compatibility is
compromised.
CFS: This is a secondary use case which is fraught with a little more
danger as it's no longer just your testing code that is 'tainted' by
this hackery. However, when you gotta do something, you gotta do it,
and it certainly sounds cleaner to me to use this hack than to change
the design if your class structure so that whatever is
programattically changing the list box also knows about all the
incoming listeners. HOWEVER, in this case there's a pretty neat
workaround: extend the Listener, and copy the addChangeListener/
removeChangeListener code, so that you have your own
ChangeListenerCollection object you can play with. That's also non-
optimal (you now have 2 ChangeListenerCollections in JS memory, which
is scarce) but it's much cleaner compared to this and the memory
impact is very small (I'll assume you won't have 5000 change
listeners!)
I have written a library-type widget extension once which really
really needed this, and extending the relevant widget wasn't a
reasonable option, because that would make use of my effect thingie
more involve than just saying: Here's a widget, go do your stuff. In
retrospect this would have been a fine solution.
see post Code at post
I use it all the time for unit tests.
cheers,
SeanC
On Apr 4, 3:16 pm, "Sandy McArthur" <sandy...@gmail.com> wrote:
Mark said "Here's an example that perhaps deserves a test. When the
user clicks
the "Go" button, the ClickListener does a large number of things. It
gets the values of many widgets, populates a Map of Java objects
with
their values, passes the Map to a GWT RPC service, gets back a List
of objects, populates a FlexTable with the data in them, enables/
disables many widgets based on the data returned, ... "
Sure the ClickListener kicks off that chain of "many things" but it
should not directly DO any of the things. If it does you have logic in
your UI and that should be avoided. Sure in that case testing things
based on that click would make sense, but what I am saying is don't do
that in the first place, and such tests become little more than
checking whether or not GWT level event handling works - not testing
any of your code.
In that same example, if the ClickListener calls
"controller.doLargeNumberOfThings()", where a separate client side GWT
class that has no UI in it, called "Controller" (to oversimply) is
present, and it handles getting values from the Model (not from
Widgets, those should be UI related and observing the Model),
populates a Map (part of the Model) and calls a GWT RPC service, etc -
then you can TEST THE CONTROLLER without having to test the GWT
ClickListener. I do "lots of things" based on a click in GWT apps all
the time, I just don't do it FROM the View?
Now I freely admit that there may be times when the "canonical" GWT
app breaks down, and you need to do something more than UI inside a UI
related class (such as having such logic in your ClickListener impl),
but this example is not one of them. This, in my opinion, is an
example of exactly NOT when to involve the UI in a "unit" test.
And again, I think the ability to test the UI at this level is
helpful, but in 98% of cases most people will be better off to
refactor their code to include a Model and Controller, in the client
as GWT classes without UI, and test that, rather than worry about
kicking off tests from the View. I was talking about the situation
where the View is a "thin veneer" observing the Model, as it really
should be.