At some point I had to create some class "Class1" and that class needs a method "method". So I have the following:
Class1MethodTest: A total of N tests that check the behavior of Class1.method
Class1 method: A full implementation of the method
But a bit later I need a different class "Class2" to have a completely similar method "method". Now I have several approaches:
Approach 1
Class1MethodTest: A total of N tests
Class1 method: Full implementation
Class2MethodTest: Another set of identical tests
Class2 method: Another full implementation
Pros: Stupid simple
Cons: not DRY
At least that's the first attempt and I might even write this before doing any refactoring, right?
Approach 2:
_hidden_private_implementation_function: Full implementation of required method
Class1MethodTest: A total of N tests
Class1 method: Call hidden_private_whatever
Class2MethodTest: Another set of identical tests
Class2 method: Also call hidden_private_stuff
Pros: DRY code, still stupid simple
Cons: Tests aren't DRY, "Test interface, not implementation"
Approach 3:
MethodTest: A total of N tests
TotallyPublicCommonMethod: Full implementation of required method
Class1MethodTest: Just one test to verify that Class1 method calls the Public one
Class1 method: Call public common method
Class2MethodTest: One more test
Class2 method: Also call common method
Pros: DRY, stupid simple
Cons: .. any other than "You're testing implementation, not interface"?
Approach 4:
This is where it gets a bit exotic. Python allows me to DRY the "Approach 3" directly:
_hidden_private_implementation_function: Full implementation of required method
makeTestForClass(cls): return a total of N tests for class cls
Class1MethodTest = makeTestForClass(Class1)
Class1 method: Call hidden_private_whatever
Class2MethodTest = makeTestForClass(Class2)
Class2 method: Also call hidden_private_stuff
Pros: DRY, "Don't test implementation"
Cons: Not that simple. Too hard to modify if I ever decide to change something in Class1.method, but not Class2.method
I can think of a couple more approaches but those are not very different from these above.
Right now I have some code looking something like "Approach 1" and I am thinking on which way I should go next to make it all cleaner and better.