| «« Superstar DJ ... Here We Go! | XAML »» |
|
About
I'm Ryan Lowe, a Software Engineering graduate living in Ottawa, Canada. I like agile software development and Ruby on Rails.
I write this blog in Canadian English and don't use a spell checker. Typos happen.
Projects
» Full-time Ruby on Rails freelancer
» Full-time with Rails since May 2005 » Former committer for RadRails (now Aptana) » I also have a few Rails side-projects in development: 1. wheretogoinTO.com Toronto nightlife 2. Hey Heads Up! TODO list and sharing 3. Layered Genealogy family history research 4. foos for foosball scoring 5. fanconcert for music fans (on hold) Hiring Rails developers? I can telecommute by the hour from Ottawa, Canada »» Email: rails AT ryanlowe DOT ca
BulletBlog
Now hosted on Hey! Heads Up -- check it out!
Syndication
Pings
Recent
Derek Lowe's (Ryan's older brother) words at Ryan's funeral
blog@ryanlowe.ca no more Forging Email Headers: Good, Bad or Ugly? Sarcastic Dictionary (Part 1 of Many) Tags Hierarchies Twisting Rails is Risky Business Risky Business? My Take on Early Alphas Whoa, it's August 2007 Closing Comments A Postscript to "Growth at the grassroots" »» All Blog Posts
Linkage
del.icio.us/ryanlowe
technorati/ryanlowe.ca/blog Aurora Roy Jim Andrew Trasker Travis Kibbee Karen Dr. Unk Ayana Van Bloggers Joel Spolsky Robert Scoble Tim Bray Dave Winer Raymond Chen James Robertson Ruby/Rails Bloggers rubyonrails.org weblog David Heinemeier Hansson Dave Thomas James Duncan Davidson Mike Clark Jamis Buck Signal vs. Noise Tobias Luetke Amy Hoy: (24)slash7 Jeremy Voorhis Eclipse Bloggers Planet Eclipse EclipseZone Luis de la Rosa Eclipse Foundation Kim Horne Billy Biggs Ian Skerrett Mike Milinkovich Bjorn Freeman-Benson Denis Roy
Archives
|
Unit Testing Multiple Interface Implementations
Sometimes you want to test several implementations of an Interface that give exactly the same results but with different performance characteristics, for example. Since they are supposed to work the same way you want to use the same tests on each of the implementations to confirm that. You could make copies of the tests for each class and manually make sure they are the same but it's a maintenance nightmare. We ran into this problem with the AudioMan project and here was our first solution, using the Java List collection as an example. Vector and ArrayList implement the List Interface. public class ListTest extends TestCase public void setList(List implementation) public void testExample1() public void testExample2() public class VectorTest extends TestCase public VectorTest() public void testExample1() public void testExample2() All of the test implementations are in ListTest. To test the Vector implementation of the List Interface, make functions of the same names in VectorTest that call the corresponding test in ListTest. This is better than copying the tests because the implementations are only in one class. But as you add new tests to ListTest you have to remember to add the respective calls to the new test to VectorTest and ArrayListTest too. It's better than keeping copies of whole tests but there's still a maintenance problem with this solution. So I was thinking about it this morning and I came up with something else: public abstract class ListTest extends TestCase protected void setUp() throws Exception protected abstract List getList(); public void testExample1() public void testExample2() public class VectorTest extends ListTest public class ArrayListTest extends ListTest With this solution, the test code is still only in one spot: the interface test class ListTest. Then each of the implementing class' test classes subclasses ListTest instead of TestCase and implements the abstract getList() method returning its own implementation of the List Interface, in this case Vector and ArrayList. All of the test methods are are inherited from ListTest, so VectorTest and ArrayListTest can be executed with JUnit. ListTest can't be run as a test because it is an abstract class. So there we have it. No more maintenance problems and it's dead easy to add a new implementation's test class. Update Sunday 12:50pm Andrew suggested I make the ListTest class abstract. I like the idea so I've changed my post. We don't run into that filtering problem with Ant because we put our tests into classifications and only run tests ending with TestA with Ant, but for other projects using the test class names to filter is a good idea. Posted at November 08, 2003 at 09:28 PM ESTLast updated November 08, 2003 at 09:28 PM EST Comments
I would suggest going one step further and making ListTest into AbstractListTest (for filtering purposes with something like Ant) and making the getList() list method abstract. But otherwise, good to go on the idear - it fits like a glove. » Posted by: aforward at November 9, 2003 07:17 AMYes, that's a great idea. Thanks buddy. » Posted by: Ryan at November 9, 2003 12:20 PMI had tried to implement the pattern for testing interfaces with JUnit that I found on: But I wasn't able to with AudioMan because the classes that we have implementing the interface are singletons -> have a static method. Since this pattern doesn't work for that, that's where it fails. But I really like your idea... much nicer than what we have. ;-) » Posted by: Jim at November 9, 2003 12:24 PMAfter reading over the document that I posted a reference to, it looks just what what you proposed. I guess that I just didn't get it when I was doing reading it the first time. damn. :-( » Posted by: Jim at November 9, 2003 02:00 PMYes indeed. :) » Posted by: Ryan at November 9, 2003 03:46 PM |