12 ms·
+1 on "well defined spec" -- a lot of Healthcare integrations are specified as "here's the requests, ensure your system responds like this" and being able to pu
by generalk 4y ago
+1 on "well defined spec" -- a lot of Healthcare integrations are specified as "here's the requests, ensure your system responds like this" and being able to put those in a test suite and know where you're at is invaluable!
But TDD is fantastic for growing software as well! I managed to save an otherwise doomed project by rigorously sticking to TDD (and its close cousin Behavior Driven Development.)
It sounds like you're expecting that the entire test suite ought to be written up front? The way I've had success is to write a single test, watch it fail, fix the failure as quickly as possible, repeat, and then once the test passes fix up whatever junk I wrote so I don't hate it in a month. Red, Green, Refactor.
If you combine that with frequent stakeholder review, you're golden. This way you're never sitting on a huge pile of unimplemented tests; nor are you writing tests for parts of the software you don't need. For example from that project: week one was the core business logic setup. Normally I'd have dove into users/permissions, soft deletes, auditing, all that as part of basic setup. But this way, I started with basic tests: "If I go to this page I should see these details;" "If I click this button the status should update to Complete." Nowhere do those tests ask about users, so we don't have them. Focus remains on what we told people we'd have done.
I know not everyone works that way, but damn if the results didn't make me a firm believer.
- andix 4y agoTDD usually means that you write the tests before writing the code. Writing tests as you write the code is just regular and proper software development.
- Spivak 4y agoOdd, I was taught TDD as 1. Write test, see that it fails the way you expect. 2. Write code that makes the test pass. 3. Write test... and be secure that you can fearlessly refactor and not backslide while you play with different ideas so long as all your tests stay green. I would get overwhelmed so fast if I just had 50 failing tests and no implementation.
- imran-iq 4y agoThat's the right way to do TDD, see this talk: https://www.youtube.com/watch?v=EZ05e7EMOLM https://www.youtube.com/watch?v=EZ05e7EMOLM One of the above comments mentions BDD as a close cousin to TDD, but that is wrong as TDD is actually BDD as you should only be testing behaviours, which allow you to "fearlessly refactor"
- deleted 4y ago[deleted]
- bcrosby95 4y agoBehavior is an unfortunate term, because the "London Style" TDD sometimes is described as testing behaviors: https://softwareengineering.stackexchange.com/questions/123627/what-are-the-london-and-chicago-schools-of-tdd https://softwareengineering.stackexchange.com/questions/1236... Which seems like the exact opposite of what the talk is saying you should do.
- pjmlp 4y agoNow do that for rendering a rotating cube in Vulkan with pbr shading.
- Spivak 4y agoThis falls under the category of problems where verifying, hell describing, the result is harder than the code to produce it. Here’s how I would do it. The challenge is that the result can’t be precisely defined because it’s essentially art. But with TDD the assertions don’t actually have to actually live in code. All we have to do is make incremental verifiable progress that lets us fearlessly make changes. So I would set up my viewport as a grid where in each square there will eventually live a rendered image or animation. The first one blank, the second one a dot, the third a square, the fourth with color, the fifth a rhombus, the sixth with two disjoint rhombuses … When you’re satisfied with each box you copy/paste the code into the next one and work on the next test always rendering the previous frames. So you can always reference all the previous working states and just start over if needed. So the TDD flow becomes 1. Write down what you want the result of the next box to look like. 2. Start with the previous iteration and make changes until it looks like what you wanted. 3. Write down what you want…
- patcon 4y agoRespectfully, I think the distinction they're making it that "writing ONE failing test then the code to pass it" is very different than "write a whole test suite, and then write the code to pass it". The former is more likely to adapt to the learning inherent in the writing of code, which someone above mentioned was easy to lose in TDD :)
- wenc 4y agoThe problem I’ve run into is that when you’re iterating fast, writing code takes double the time when you also have to write the tests. Unit tests are still easy to write but most complex software have many parts that combine combinatorially and writing integration tests requires lots of mocking. This investment pays off when the design is stable but when business requirements are not that stable this becomes very expensive. Some tests are actually very hard to write — I once led a project that where the code had both cloud and on-prem API calls (and called Twilio). Some of those environments were outside our control but we still had to make sure they we handled their failure modes. The testing code was very difficult to write and I wished we’d waited until we stabilized the code before attempting to test. There were too many rabbit holes that we naturally got rid of as we iterated and testing was like a ball and chain that made everything super laborious. TDD also represents a kind of first order thinking that assumes that if the individual parts are correct, the whole will likely be correct. It’s not wrong but it’s also very expensive to achieve. Software does have higher order effects. It’s like the old car analogy. American car makers used to believe that if you QC every part and make unit tolerances tight, you’ll get a good car on final assembly (unit tests). This is true if you can get it right all the time but it made US car manufacturing very expensive because it required perfection at every step. Ironically Japanese carmakers eschewed this and allowed loose unit tolerances, but made sure the final build tolerance worked even when the individual unit tolerances had variation. They found this made manufacturing less expensive and still produced very high quality (arguably higher quality since the assembly was rigid where it had to be, and flexible where it had to be). This is craftsman thinking vs strict precision thinking. This method is called “functional build” and Ford was the first US carmaker to adopt it. It eventually came to be adopted by all car makers. https://www.gardnerweb.com/articles/building-better-vehicles-via-functional-build https://www.gardnerweb.com/articles/building-better-vehicles...
- pmarreck 4y ago> writing code takes double the time when you also have to write the tests this time is more than made up for by the usual subsequent loss of debugging, refactoring and maintenance time, in my experience, at least for anything actively being used and updated
- tsimionescu 4y agoThere are two problems I've seen with this approach. One is that sometimes the feature you implemented and tested turns out to be wrong. Say, initially you were told "if I click this button the status should update to complete", you write the test, you implement the code, rinse and repeat until a demo. During the demo, you discover that actually they'd rather the button become a slider, and it shouldn't say Complete when it's pressed, it should show a percent as you pull it more and more. Now, all the extra care you did to make sure the initial implementation was correct turns out to be useless. It would have been better to have spent half the time on a buggy version of the initial feature, and found out sooner that you need to fundamentally change the code by showing your clients what it looks like. Of course, if the feature doesn't turn out to be wrong, then TDD was great - not only is your code working, you probably even finished faster than if you had started with a first pass + bug fixing later. But I agree with the GP: unclear and changing requirements + TDD is a recipe for wasted time polishing throw-away code. Edit: the second problem is well addressed by a sibling comment, related to complex interactions.
- generalk 4y ago> Say, initially you were told "if I click this button the status should > update to complete", you write the test, you implement the code, rinse and > repeat until a demo. During the demo, you discover that actually they'd > rather the button become a slider, and it shouldn't say Complete when it's > pressed, it should show a percent as you pull it more and more. Now, all the > extra care you did to make sure the initial implementation was correct turns > out to be useless. Sure, this happens. You work on a thing, put it in front of the folks who asked for it, and they realize they wanted something slightly different. Or they just plain don't want the thing at all. This is an issue that's solved by something like Agile (frequent and regular stakeholder review, short cycle time) and has little to do with whether or not you've written tests first and let them guide your implementation; wrote the tests after the implementation was finished; or just simply chucked automated testing in the trash. Either way, you've gotta make some unexpected changes. For me, I've really liked having the tests guide my implementation. Using your example, I may need to have a "percent complete" concept, which I'll only implement when a test fails because I don't have it, and I'll implement it by doing the simplest thing to get it to pass. If I approach it directly and hack something together I run the risk of overcomplicating the implementation based on what I imagine I'll need. I don't have an opinion on how anyone else approaches writing complex systems, but I know what's worked for me and what hasn't.