5 ms·
Part of test-driven design is using the tests to drive out a sensible and easy to use interface for the system under test, and to make it testable from the get-
by lukeramsden 3y ago
Part of test-driven design is using the tests to drive out a sensible and easy to use interface for the system under test, and to make it testable from the get-go (not too much non-determinism, threading issues, whatever it is). It's well known that you should likely _delete these tests_ once you've written higher level ones that are more testing behaviour than implementation! But the best and quickest way to get to having high quality _behaviour_ tests is to start by using "implementation tests" to make sure you have an easily testable system, and then go from there.
- Dylan16807 3y ago> It's well known that you should likely _delete these tests_ once you've written higher level ones that are more testing behaviour than implementation! Is it? I don't think I've ever seen that mentioned.
- MoreQARespect 3y ago>It's well known that you should likely _delete these tests_ once you've written higher level ones that are more testing behaviour than implementation! Building tests only to throw them away is the design equivalent of burning stacks of $10 notes to stay warm. As a process it works. It's just 2x easier to write behavioral tests first and thrash out a good design later under its harness. It mystifies me that doubling the SLOC of your code by adding low level tests only to trash them later became seen as a best practice. It's so incredibly wasteful.
- lmm 3y agoWhat exactly is it wasting? Is your screen going to run out of ink? Even in the physical contruction world, people often build as much or more scaffolding as the thing they're actually building, and that takes time and effort to put up and take down, but it's worthwhile. Sure, maybe you can do everything you would do via TDD in your head instead. But it's likely to be slower and more error-prone. You've got a computer there, you might as well use it; "thinking aloud" by writing out your possible API designs and playing with them in code tends to be quicker and more effective.
- MoreQARespect 3y ago>What exactly is it wasting? Time. Writing and maintaining low level unit tests takes time. That time is an investment. That investment does not pay off. Doing test driven development with high level integration tests also takes time. That investment pays dividends though. Those tests provide safety. >Sure, maybe you can do everything you would do via TDD in your head instead. But it's likely to be slower and more error-prone. It's actually much quicker and safer if you can change designs under the hood and you dont have to change any of the tests because they validate all the behavior. Quicker and safer = you can do more iterations on the design in the available time = a better design in the end. The refactoring step of red, green, refactor is where the design magic happens. If the refactoring turns tests red again that inhibits refactoring.
- spinningslate 3y agoDon't agree, though I think it's more suble than "throw away the tests" - more "evolve them to a larger scope". I find this particularly with web services,especially when the the services are some form of stateless calculators. I'll usually start with tests that focus on the function at the native programming language level. Those help me get the function(s) working correctly. The code and tests co-evolve. Once I get the logic working, I'll add on the HTTP handling. There's no domain logic in there, but there is still logic (e.g. mapping from json to native types, authentication, ...). Things can go wrong there too. At this point I'll migrate the original tests to use the web service. Doing so means I get more reassurance for each test run: not only that the domain logic works, but that the translation in & out works correctly too. At that point there's no point leaving the original tests in place. They're just covering a subset of the E2E tests so provide no extra assurance. I'm therefore with TFA in leaning towards E2E testing because I get more bang for the buck. There are still places where I'll keep native language tests, for example if there's particularly gnarly logic that I want extra reassurance on, or E2E testing is too slow. But they tend to be the exception, not the rule.
- munch117 3y ago> At that point there's no point leaving the original tests in place. They're just covering a subset of the E2E tests so provide no extra assurance. They give you feedback when something fails, by better localising where it failed. I agree that E2E tests provide better assurance, but tests are not only there to provide assurance, they are also there to assist you in development.
- MoreQARespect 3y agoStarting low level and evolving to a larger scope is still unnecessary work. It's still cheaper starting off building a playwright/calls-a-rest-api test against your web app than building a low level unit test and "evolving" it into a playwright test. I agree that low level unit tests are faster and more appropriate and if you are surrounding complex logic with a simple and stable api (e.g. testing a parser) but it's better to work your way down to that level when it makes sense, not starting there and working your way up.
- jt2190 3y ago> As a process it works. It's just 2x easier to write behavioral tests first and thrash out a good design later under its harness. I think this “2x easier” only applies to developers who deeply understand how to design software. A very poorly designed implementation can still pass the high level tests, while also being hard to reason about (typically poor data structures) and debug, having excessive requirements for test setup and tear down due to lots of assumed state, and be hard to change, and might have no modularity at all, meaning that the tests cover tens of thousands of lines (but only the happy path, really). Code like this can still be valuable of course, since it satisfies the requirements and produces business value, however I’d say that it runs a high risk of being marked for a complete rewrite, likely by someone who also doesn’t really know how to design software. (Organizations that don’t know what well designed software looks like tend not to hire people who are good at it.)
- deleted 3y ago[deleted]
- MoreQARespect 3y ago"Test driven design" in the wrong hands will also lead to a poorly designed non modular implementation in less skilled hands. I've seen plenty of horrible unit test driven developed code with a mess of unnecessary mocks. So no, this isnt about skill. "Test driven design" doesnt provide effective safety rails to prevent bad design from happening. It just causes more pain to those who use it as such. Experience is what is supposed to tell you how to react to that pain. In the hands of junior developers test driven design is more like test driven self flagellation in that respect: an exercise in unnecessary shame and humiliation. Moreover since it prevents those tests with a clusterfuck of mocks from operating as a reliable safety harness (because they fail when implementation code changes, not in the presence of bugs), it actively inhibits iterative exploration towards good design. These tests have the effect of locking in bad design because keeping tightly coupled low level tests green and refactoring is twice as much work as just refactoring without this type of test.
- User23 3y ago> I've seen plenty of horrible unit test driven developed code with a mess of unnecessary mocks. Mocks are an anti-pattern. They are a tool that either by design or unfortunate happenstance allows and encourages poor separation of concerns, thereby eliminating the single largest benefit of TDD: clean designs.
- User23 3y agoPut simply, doing TDD properly leads to sensible separation of concerns.