• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Pragmatic Unit Testing in Java 8 with JUnit - Test Strategy

 
Ranch Hand
Posts: 192
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi and good luck with the book.

Does the book come with a case study that shows testing and if so is this like a 'walking skeleton' approach where layers of testing are gradually added with functionality?

Thanks Kevin.
 
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The book does have plenty of examples and although it could be my own biased perception, it does seem like incremental development of tests is implied as I read through it. Chapter 12 is dedicated to the discussion of Test-Driven Development but I can find no direct reference to Alistair Cockburn's "walking skeleton" metaphor.
 
Greenhorn
Posts: 8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I had been thinking about TDD myself, and its relation to unit testing. In TDD, the big thing is to develop all your tests in order to use them to validate that as your code is written, it meets the designed behavior as codified in the tests. You're done when all of the tests pass, assuming you've done things like have your code peer-reviewed, fully documented, etc.

The more I thought about it though, the more I came to the realization that TDD, as defined, cannot be applied to unit testing unless you are superbly clairvoyant! Why? Well, if one of the criteria of unit testing is to test all code paths in a given unit of code (class, method, whatever), in order to apply TDD, one would have to know what the code paths are before the code was written, so you could create the tests to drive them. That's a really hard task for a typical mortal.

That's not the case for higher order testing where a design dictates the code behavior, allowing for tests to be written against the expected behaviors, rather than written against a priori knowledge of the code's internals.
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
When I mentor other developers at work about TDD, I often share this quote from E. W. Dijkstra:

Edsger W. Dijkstra wrote:Program testing can be used to show the presence of bugs, but never to show their absence!


At the end of a TDD cycle (Red, Green, Refactor), I propose an alternative question to ask that's a natural extension of this quote: "What's the next bug I want to reveal?" and missing functionality is considered a bug. We're done when we can't think of a way to reveal additional bugs. This, of course, does not guarantee that we've not missed a bug or two but it does put us in a mindset that's in line with Dijkstra's treatment of tests.
 
author
Posts: 799
5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There are definitely some good thoughts/questions in this thread.

The book, as a rewrite of the first edition, promotes an incremental test-after approach. Write a bit of code, devise and code the many ways you want to probe at it with tests, and perhaps clean the code up a bit as you consider the design. Move on to the next increment of code. It's not strictly test-driven, though as mentioned, there's a chapter on TDD (which is predominantly what I do).

The walking skeleton approach isn't discussed; I view that more as an architectural-level pattern design to help you quickly put up a minimal implementation of the end-to-end system, then incrementally grow on it. You'll certainly want to do unit testing along the way as you do.

John--indeed, TDD is not so much a unit testing strategy as a design strategy--it constrains the testing to what you're about to build into the system. When doing TDD, I strive to keep my implementation as minimally sufficient as possible. For example, if I'm writing a routine that returns the average of a list of numbers, I'll start with an empty list, in which case the implementation is to return 0. Then I write a test that puts a single number into the list, and code that, and finally multiple numbers in the list. I end up with three tests. The short-cut route--since writing an "average" function is so trivial--is to just write one test and code it up... but that doesn't force the issue of increasing the test coverage like the test-driven approach does.

Regards,
Jeff
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

John Czukkermann wrote:The more I thought about it though, the more I came to the realization that TDD, as defined, cannot be applied to unit testing unless you are superbly clairvoyant! Why? Well, if one of the criteria of unit testing is to test all code paths in a given unit of code (class, method, whatever), in order to apply TDD, one would have to know what the code paths are before the code was written, so you could create the tests to drive them. That's a really hard task for a typical mortal.


I disagree. I'm pretty sure there are no immortals among the legions of TDD practitioners who are happily writing good code and coming up with well-factored designs. A substantial portion of Jeff's book is spent explaining mnemonics like FIRST, Right-BICEP, and CORRECT as ways to remember the different angles of testing with which you can approach your code to account for various quality attributes.

I do think that whenever TDD is discussed, BDD and ATDD should also be mentioned as these are higher-level in nature, with a sharper focus on functionality rather than internal implementation. Effective TDD actually should focus more on methods and behavior rather than implementation details and I find that the more robust and useful tests that I write do have that kind of focus. In fact, I'm currently reading the Cucumber book also published by the Pragmatic folks, and it does make a reference to the "walking skeleton" that the OP mentioned. Cucumber is one of, if not the most popular tools for doing BDD/ATDD.

One other tool I use to become more "clairvoyant" about testing: storyboards. I just mentioned this to another rancher and cited Cay Horstmann's book, Big Java Late Objects, (Section 4.6) -- I also like Jeff Patton's "User Story Mapping" book. I find that a good, deep understanding of what you want your program to do usually goes a long way in figuring out different kinds of scenarios you want to test to provide good code/test coverage.
 
John Czukkermann
Greenhorn
Posts: 8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I am happy to coexist in disagreement with you Junilu. Fundamentally, I think we are in agreement because my post was not intended to slight TDD at all. It is certainly a valuable methodology, just not workable as it was originally proposed, when applied to low-level unit testing as I described it. I am absolutely all in for employing TDD for functional verification testing such as code delivering consumable externals where a specification is available up-front, against which both the test cases and the code can be developed. And I don't limit the applicability of TDD to that rather small scope either.

The reason I focused on a rather constrained definition of unit testing is because often times, one cannot externally drive certain code paths a complex methods very easily, such as those that result from complex interactions with other components or as in the case of handling exceptions. Such cases require at least some code to be present so you know how to drive execution down that path. We should also be practical about our unit testing and not think that the benefit of getting to 100% unit test coverage justifies the expense in all cases. I think that focusing on methods that have a high degree of complexity does warrant the effort though, because that's where defects will generally be found. Especially code that deals with checking for edge cases and input validation.

I guess what I'm saying is that unit testing of isolated methods such that the execution and behavior of critical, if not all, code paths can be achieved, is about as low on the testing food chain as one can get, and at that level TDD in it's pure sense is not practical.

I probably should have been a little less tongue-in-cheek when writing my original reply. ;-)
 
Jeff Langr
author
Posts: 799
5
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi John,

I've found that a mostly-test-driven approach has indeed been practical, on very large and very complex systems and many others of different stripes I've worked with in the past 15 years, and perhaps partly because it promotes *eschewing* methods with a high degree of complexity. Most complexity in software can and should be driven out in favor of small, composed methods and rare but isolated complex methods.

I've also found that with TDD, it's a net gain, not an expense. The refactored code and tests allow me to develop more rapidly, not less, particularly as the size of the system grows. And I don't ship "low on the food chain" logic errors, it almost never happens anymore (the defects I do ship are usually integration related), so I'm not paying large unknown unquantified costs for such defects. YMMV.

Regards,
Jeff
 
John Czukkermann
Greenhorn
Posts: 8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jeff Langr wrote:
I've found that a mostly-test-driven approach has indeed been practical, on very large and very complex systems and many others of different stripes I've worked with in the past 15 years, and perhaps partly because it promotes *eschewing* methods with a high degree of complexity. Most complexity in software can and should be driven out in favor of small, composed methods and rare but isolated complex methods.



I'm in violent agreement with you on this one Jeff! Few things bother me more than having to maintain or review code where every method takes four screens to get through and management's philosophy for legacy code is not to fix it if it isn't broken outright.
 
Jeff Langr
author
Posts: 799
5
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks John.

aha! Feather's book Working Effectively With Legacy Code, though a little dated, is still good fodder for getting legacy code under control. The Mikado Method also is a great disciplined approach for larger attempts at cleaning up legacy code.

"If it ain't broke, don't fix it" is how most systems got to be so bad. Having fast unit tests can help a lot here.

Regards,
Jeff
 
Consider Paul's rocket mass heater.
reply
    Bookmark Topic Watch Topic
  • New Topic