|John Fallows||May 10, 2006 5:23 pm|
|Adam Winer||May 10, 2006 5:43 pm|
|Dennis Byrne||May 10, 2006 6:29 pm|
|Adam Winer||May 10, 2006 9:44 pm|
|Craig McClanahan||May 10, 2006 10:09 pm|
|John Fallows||May 12, 2006 9:08 pm|
|Craig McClanahan||May 12, 2006 9:15 pm|
|John Fallows||May 12, 2006 9:16 pm|
|Craig McClanahan||May 12, 2006 9:24 pm|
|John Fallows||May 12, 2006 10:12 pm|
|Craig McClanahan||May 12, 2006 10:32 pm|
|John Fallows||May 12, 2006 11:43 pm|
|Adam Winer||May 13, 2006 9:08 am|
|Martin Marinschek||May 13, 2006 2:46 pm|
|John Fallows||May 13, 2006 3:44 pm|
|Craig McClanahan||May 13, 2006 4:49 pm|
|John Fallows||May 14, 2006 11:14 am|
|Adam Winer||May 14, 2006 12:35 pm|
|John Fallows||May 14, 2006 9:47 pm|
|Matthias Wessendorf||Jun 19, 2006 3:31 pm|
|Subject:||Re: Thoughts about unit testing and mock objects|
|From:||Matthias Wessendorf (mat...@apache.org)|
|Date:||Jun 19, 2006 3:31:02 pm|
since there is this nice little guy (), I'd like to come back to this discussion.
Adam and I spoke about starting to solve this problem. For "JSF infrastructure" things we'd like to give Shale a try.
For stuff like UIComponent / Rendere there might be JMock useful. Btw. I checked the legal-discuss list and the jMock license (BSD style) fit's fine into the ASF world.
On 5/14/06, John Fallows <john...@gmail.com> wrote:
On 5/14/06, Adam Winer <awi...@gmail.com> wrote:
On 5/14/06, John Fallows <john...@gmail.com> wrote:
On 5/13/06, Craig McClanahan <crai...@apache.org> wrote:
In an example that ought to be relevant for this codebase :-), consider what happens when you want to test the encodeBegin(), encodeChildren(), and encodeEnd() methods of a Renderer implementation. The methods return void, so the output isn't particularly interesting :-). What is interesting, however, is that the methods themselves are going to assume that FacesContext.getResponseWriter() returns somethng usable, even in a unit test scenario. Shale's MockFacesContext, for example, gives you back a mock writer that can write out to a buffer, which you can later examine to determine whether the component created correct markup or not (based on the properties/attributes you set on the component). And, this functionality is needed in all renderer tests, so it makes sense to encapsulate into a separate library that can be debugged once.
How do you approach this sort of scenario with dynamic mocks?
Nice example, thanks Craig!
With dynamic mock objects, you would setup each method call made on the ResponseWriter which is functionality equivalent, although much more verbose.
Far more so, to the point that I can hardly imagine anyone writing a test like that. Also, the order of invocations becomes partially important, and partially irrelevant - which attributes are written on which elements are important, which order the attributes are written is irrelevant. A dynamic mock is never going to have enough semantics to capture this requirement.
Surprisingly, jMock does have sufficient semantics, but the ResponseWriter buffer approach makes it easier to specify the expected output.
That's why the ADF Faces codebase has a handwritten mock for
a response writer that very intentionally *is not* the same response writer we use at runtime:
... and there's no way that the default mock implementation would capture any of the semantics that this test captures.
Since you mention this example, the TestResponseWriter seems to include some implementation-specific stuff, such as optimized removal of empty span elements in the final markup. For isolated unit testing, we probably ought to be unit testing the Renderers to verify that they interact as designed with the mock ResponseWriter, and then separately unit testing our ResponseWriter implementation to make sure it optimizes correctly.
Your point is to simplify the effort involved in verifying the result,
right? That's pretty useful, although this example doesn't appear to address my question about the behavior that might become incorrect.
Simplifying the effort to write a test is crucial, not just pretty useful. Developers are habitually lazy at writing tests, and putting obstacles in their path ensures they won't.
Sure. Like I said above, buffering the Renderer output is a good idea that makes it easier to verify the output.
For your question about "behavior that might be incorrect",
say I was mocking UIComponent behaviors for a piece of code that will need to invoke findComponent(). How do I mock findComponent()? Odds are, if I'm writing the test, I mock it to produce exactly the result I want - not necessarily implement the correct algorithm. Do I know that my test is right?
Thanks for the example, this is getting closer to what I'm trying to figure out. :-)
Suppose there are two codepaths in such a unit test, one that expects a non-null component from findComponent, and another codepath that handles the null case.
As a unit test writer, I don't need to be concerned about how findComponent is actually implemented, just that there are only two possible results affecting the codepath, null and non-null. If the unit tests are isolated, then there would be a separate unit test for UIComponentBase to verify the implementation of findComponent.
In general, I *think* the only behavior that is relevant to each unit test is the implementation of the method under test. All other participants in that implementation are mocked to control the codepath during execution, giving an opportunity to provide 100% codepath coverage over several unit tests.
tc, -john. -- http://apress.com/book/bookDisplay.html?bID=10044 Author: Pro JSF and Ajax: Building Rich Internet Components, Apress
-- Matthias Wessendorf Aechterhoek 18 48282 Emsdetten blog: http://jroller.com/page/mwessendorf mail: mwessendorf-at-gmail-dot-com