I'm still learning to write great code like everyone else. Recently there's been some articles bashing TDD. I would like to share what worked for me with TDD.
Good OOP should follow SOLID and clean code principles. Classes should be small and methods even smaller. This makes writing unit tests much easier as there is much less to test per test case.
Clean code by Uncle Bob and POODR by Sandi Metz both dedicate large chapters to testing. This is not by accident. Test driven development is a huge part of writing maintainable code. If you are having problems with test driven development, perhaps your classes and methods are too long. Consider whether or not your code follows SOLID and clean code principles.
Before TDD, what I did was create a class and define the attributes and methods that I think I would need. I found this habit hard to break, until I forced myself to create test cases before I even wrote one line of code. How I did this was to create easier unit tests.
def test_myobject: #check if the attribute exists self.assertTrue(hasattr(myobject, 'myattribute')) #check the type self.assertIsInstance(myobject.mylist, list) #check if function self.assertTrue(callable(myobject.mymethod) #check if attribute defaults to False self.assertFalse(myobject.my_false_attribute)
This is very similar to creating the basic structure of a class without writing the actual class. Some would argue that these unit tests do not test anything and are not worth it. After all why would you test basic language concepts like variable assignment?
I would say an object's public API is a contract and should be tested. Any change of these attributes will break code. This would be an easy fix, but also these unit tests are easy to write. This make it almost free.
Have your class and unit tests side by side when writing and updating code. If you update one, update the other.
If your constructors contain any conditional assignment logic, then you should definitely write unit tests for this.
I only write unit tests for methods that are public. If your private methods contain some tricky logic that needs to be tested, perhaps consider creating a mixin class for that method and exposing it publicly.
If your test cases import a bunch of other classes, that can show you are testing a too much. Practice DRY (Don't repeat yourself) in testing code as well. Use mocks and stubs in replace of other objects that your unit tests depend on instead of importing other objects.
Good test cases have made my flow much better. Now I can rapidly design and add new features and they mostly work on first run. If I do have errors, the issue is with integration and not the single object itself. Refactoring is also easier as I know when I break stuff.
This is what worked for me. Good luck on your journey and help others along the way!