TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Ask HN: Seriously, how do you TDD?

68 pointsby rcd2over 2 years ago
Although I understand its mantra, I was never able to “drive the design” using TDD. To highlight my issues, let me share a recent failed experiment.<p>I have been trying to create a command line tool to categorise my expenses and show a total by category. I’ve been using this exercise to practice TDD.<p>In my latest frustrated attempt, my first test was along the lines of “it can categorise a single transaction correctly”. The problem with this is that this is testing the “tip of the iceberg” that will be almost the whole, final app. I think I was attempting a more “top down” design strategy here.<p>Before that one, I tried the opposite: a more “bottom up” approach. I know I need to, at least, read a CSV, parse a CSV row into some data structure and then categorise it. So I TDDed my way through to complete these “sub problems”. However, when I was about to apply the categorisation rules I realised that the data structure I created didn’t help, was just bad and didn’t work at all :).<p>So, how do you do it when you have a “larger project” (here, “larger” means something that’s not your typical “2&#x2F;3 points user story” at work)? I always end up feeling stuck whether I try bottom-up or top-down.

46 comments

softwaredougover 2 years ago
Use whatever feedback loop is most efficient (speed &#x2F; accuracy).<p>Sometimes that&#x27;s building a full app and getting it to market. Sometimes that&#x27;s isolating a bug in a legacy system to a unit test. Sometimes its the compiler failing to build your code. Sometimes it&#x27;s a data science problem, and it means training a model in a notebook, to measure its accuracy.<p>Don&#x27;t get hung up on using one particular feedback loop.<p>Getting stuck in the wrong feedback loop is a big anti pattern of software development. When it takes forever to submit a job to some system, then takes 20 minutes to give you a type error in a scripting language, that could be caught by a linter or type checker sooner. Those 20 minutes easily turn into 60 minutes of distraction. Imagine instead you could catch this in 5 seconds with a test &#x2F; linter &#x2F; whatever. Then you&#x27;d resolve the issue in minutes. OTOH spending hours decomposing simple code into unit tests can be a waste of time, when the most value is submitting that simple code to a batch processing system, to see if it produces output...
评论 #32745500 未加载
kazinatorover 2 years ago
You can&#x27;t &quot;drive the design&quot; with TDD. To write a failing test, you have to have a requirement which is encoded by the test. That requirement doesn&#x27;t come from TDD, and TDD can&#x27;t tell you how to procure it.<p>To go from a high level requirement like &quot;command line application to track transactions and show expenses by category&quot;, you need to go through several levels of detailed design before you have anything that is implementable by a test-driven approach, which will necessarily be bottom-up.<p>Remember that tests can only confirm the presence of a defect, not its absence, and that not everything can be tested due to the intractability of exhaustive testing.<p>By blindly following test-driven design, you could end up with a useless, inflexible program that correctly categorizes expenses over only a small, fixed vocabulary of categories because it never occurred to anyone that a category must be something user-defined.<p>A user-defined category feature cannot be implemented with strict TDD because the space of user-defined categories is infinite. Say you have a test which confirms operation for every valid category string up to three characters long. That doesn&#x27;t prove the system will not fail for a four-character-long category.<p>The only people who regard TDD as some kind of panacea are those with no formal background of any kind, who don&#x27;t understand the role of testing in the overall software engineering context, and what its limitations are.
评论 #32714502 未加载
评论 #32736976 未加载
评论 #32715586 未加载
jodohertyover 2 years ago
Don&#x27;t think in terms of tests -- think in terms of examples.<p>Write examples of how you would like to be able to solve common cases and handle edge cases if you had the ideal interface based on what you know at the time, then use unit tests with stubbed out interfaces as a tool while trying to develop the implementation.<p>Refine and even rewrite your examples as you learn about what works and what doesn&#x27;t work.<p>Recursively break down complex pieces into smaller modules as you learn what can be handled independently and composed with the other pieces. Break off independent examples depicting how you expect those modules to work and be used as new unit test suites for those new modules.<p>Don&#x27;t sweat testing the implementation details, especially when prototyping. What you want is to use tests as a way to speed up your development process and improve the quality of your design and architecture.<p>Try to write tests that show off how nice your code is to use and how few edge cases you need to handle when calling it. It&#x27;ll raise the bar for how your code interacts with the rest of the application or system and give you smaller pieces to work and focus on.<p>Finally, for really green field stuff with a lot of unknown unknowns, you often have to write a lot of standalone, bottom-up throw-away prototypes for things. That doesn&#x27;t invalidate the usefulness of tests, so don&#x27;t be afraid to do that pretty regularly.
评论 #32716116 未加载
评论 #32745523 未加载
windows2020over 2 years ago
A year into a novel project, a teammate expressed their concern about a lack of unit tests. They&#x27;d even spent time creating a bunch early on. I asked if they still ran or were even relevant any longer and the answer was no.<p>Particularly with exploratory projects, I&#x27;d recommend focusing on rapidly iterating to reach the simplest solution (businesswise and implementation-wise).<p>If in the end non-trivial logic must be specified, write some tests. Your definition of &#x27;non-trivial&#x27; will change with experience.
评论 #32715273 未加载
评论 #32714871 未加载
jdlshoreover 2 years ago
A lot of people giving their opinion about TDD, but not many actually answering your question.<p>The answer depends on your approach. You can go outside-in (top-down) or inside-out (bottom-up). The one you use is a matter of taste. A lot of people find outside-in easier, but inside-out can be a bit faster if you have a good sense of how your system will fit together.<p>To take an outside-in approach, start at the top level of your application. Possible just under the UI, because UIs can be hard to test. (If the UI isn’t tested, make it do as little work as possible, and have it just forward to the tested code to do real work.)<p>Test-drive a module, class, or method in that top layer. The smaller the better. If there’s something complicated that should be handled by another module&#x2F;class&#x2F;function&#x2F;method, make the module&#x2F;function&#x2F;etc., but just hardcode one answer. Make a note of the things you hardcode.<p>When you’ve finished TDDing your first thing, look at your notes and choose something that you hardcoded. TDD that in the same way. Repeat recursively until the application works. Focus on getting to a working but incomplete application as soon as possible, by working depth-first instead of breadth-first.<p>Once you have a working but incomplete application—a “walking skeleton”—think of a feature you’re missing and add it in the same way. Continue until the application is done. Remember to look for opportunities to improve the design and refactor as you go.<p>Inside-out is very similar, except that you do the depth-first recursion in your head, mentally, and don’t write any code until you get to the leaves of the recursion tree. You test-drive the leaf, then test-drive its caller, etc., until you get to the top. It takes more experience, more thinking about design during TDD, and more willingness to refactor.<p>I have videos on my site if you’d like more. Start with this series:<p><a href="https:&#x2F;&#x2F;www.jamesshore.com&#x2F;v2&#x2F;projects&#x2F;lunch-and-learn" rel="nofollow">https:&#x2F;&#x2F;www.jamesshore.com&#x2F;v2&#x2F;projects&#x2F;lunch-and-learn</a>
评论 #32721989 未加载
risover 2 years ago
Don&#x27;t let TDD destroy your architecture.<p>TDD encourages a form of programming where you build from the outside-in, building layer after layer of abstraction, putting off solving the actual problem until you get to the messy gooey centre where it ends up being a kludge. It&#x27;s also very easy to make flawed assumptions in your original TDD spec which you don&#x27;t realize until you <i>get</i> to that gooey centre, having wasted N days in the process.<p>I honestly don&#x27;t tend to start anything until I have a <i>basic</i> idea of how it&#x27;s going to work (not a fashionable idea these days), and find that projects end up with a much saner structure when they&#x27;re built from the inside out.<p>So my preference is to start TDD once I&#x27;ve got a very basic version of something working, at which point you can start diving in to all the corner cases.
评论 #32714809 未加载
notaspecialistover 2 years ago
In all my years of coding, I only knew one developer who did TDD properly. He taught me, as I was struggling trying to track down an intermittent bug and nothing was working. Not going to lie -learning to do TDD properly is difficult. Since learning it, I&#x27;ve not met another dev who does it, and most devs barely write tests. Stick with it, practice. You&#x27;ll eventually hit that ahah! moment.<p>A story: I used to work with some devs, most of them would finish coding something in 2 weeks and then spend a week debugging it. I&#x27;d take 4 weeks to TDD something. My boss was frustrated at my speed until one day a production system which I&#x27;d written, stopped working. Several million dollars was stuck in limbo, the backend team responsible for the paperwork unable to do anything. We had a new dev and the bandwagon was &quot;TDD is crap!&quot; was fully rolling that morning. I asked the backend processing person what had changed? The said we had a new client, they had a weird name. Looked into the client log, sure enough. Added a test with the client&#x27;s name, and ran the tests. It spat out &quot;1 failing test in module X at line Y because &#x27;this&#x27;&quot;. Modified the code to handle that test, re-ran the tests, 100% pass. Emailed the results output to the team for a manual upload, processing done, committed and pushed to the production branch. The server would update after business hours. Diagnosed and fixed in 5 minutes. I turned to my colleague who I knew was still stepping through his code for 2 days and said (within earshot of the new dev) &quot;How&#x27;s that bug you&#x27;ve been hunting? Still not found it? This is the power of TDD. I might be slow at the start, but I fix bugs quickly&quot;. The dev manager had everyone take a day of TDD training after that, but nobody really understood. The trainer said my approach to TDD is too strict, with my team doing 110 test runs against the next team of 63 test runs. Our solution had only a few lines of code verses the paragraphs of the others though.
评论 #32722977 未加载
评论 #32739609 未加载
electromechover 2 years ago
&gt; my first test was along the lines of “it can categorise a single transaction correctly”... I was attempting a more “top down” design strategy here.<p>Sounds right to me! I usually start projects&#x2F;features with a test that covers the end-to-end behavior I want to see. When you have no other tests, a smoke test is the highest value test you can add because it measures the thing you care about.
avl999over 2 years ago
Only the most religious TDD evangelists will force you to &quot;drive the design&quot; using tests. That advice is not relevant for most developers except for perhaps absolute beginners who don&#x27;t have the experience or mental models to determine what a good design is and TDD might force them to come up with a decent design by the sheer fact of making their design testable, which alone goes a long way (but not necessarily all the way) in making a &quot;good design&quot;.<p>Most experience developers will come up with the architecture&#x2F;design of what they are working on through a combination of intuition and experience, write tests after and then use TDD to fill the gaps once they have nailed down the architecture.<p>I do TDD some of the time and my workflow often look like this:<p>1. Start implementing an object&#x2F;class, defining APIs of its key methods while having a higher level picture in my head of how it will fit with other components<p>2. Implement a good chunk if not all of that object (not more than a few 100 lines typically)<p>3. Write a test to verify that what I did worked.<p>4. Start commenting out parts of the code I wrote to make sure the applicable test cases fail. Uncomment after verification is done.<p>5. Use TDD to cover and implement edge case handling and anything leftover<p>6. Repeat 1-5 for each class&#x2F;component&#x2F;module until done<p>I only get into TDD once the architecture and high level design are finalized, and I have some code written for them and I have some reasonable level of confidence that it is not gonna drastically change. I also generally go &quot;bottom-up&quot; starting from the leafs of the object graph up and use TDD maybe ~50% of the time (just a rough guess).<p>Trying to force yourself to let &quot;TDD drive the design&quot; will likely result in frustration as you have experienced and slowdown.
jvlakeover 2 years ago
If you do ‘red, green, refactor’ TDD and write clean ‘arrange, act, assert’ tests then you’ll likely create nice single responsibility classes that are a pleasure to test and extend.<p>The tests usually run lightning quick because the units will be small.<p>When bugs occur in your codebase it will be at the right level of abstraction to write tests for the bug.<p>I almost never do strict TDD, I hack away to get something going and use the tests to refactor the working code into clean srp code. The end result is the same. However where I would use strict TDD is when there is massive complexity (like creating a video encoder) or if you are following a published specification.<p>So ‘just barely working, green, refactor, else red, refactor, green’ is probably closer to my actual day-to-day. The working code is clean, the tests are clean, and the coverage is good. The order doesn&#x27;t matter.
itrollpussiesover 2 years ago
I am not trolling today, TDD doesn&#x27;t make any sense to me, testing to fail and correcting it until it passes doesn&#x27;t work with my flow.<p>In a DAW(Digital Audio Workstation), this is like adjusting the knobs, adding effects when you haven&#x27;t even added any samples, it just doesn&#x27;t make any sense, stop forcing yourself to do things that doesn&#x27;t align with your flow, you&#x27;ll likely get annoyed for no reason.<p>The way I work is I do the architecture design, I write the code, then I write my test by following the design graph.<p>If I don&#x27;t know what the design would look like, I do what I call &quot;code freestyling&quot; to get a basic idea of what the overall design would look like then I repeat the above step.
评论 #32719205 未加载
wildeover 2 years ago
I used to work with a lot of ex-Pivotal folks. Their workflow was something like:<p>1. Talk through the requirements and write down some key bullets<p>2. Whiteboard some design options. Make sure you have at least 2 interesting ones.<p>3. Prototype them out. Decide on your favorite.<p>4. Delete the prototype.<p>5. Start writing tests now that you know where you want to be, and TDD from there.<p>The key point is that TDD has to start from a relatively defined sense of what you want. If you don’t have that it’s totally fine explore. Just don’t ship that code.
peteradioover 2 years ago
What is wrong with your first test? Find some way to persist the result if you like it and make a test that checks that you can still get that result. Now you can sanity check by running the test to make sure it didn&#x27;t break. That is one benefit of having the test, did something new break something old. Generally that is as far as I take TDD. While writing new routines, it is nice to have a test calling those routines rather than maintain whole executables because many IDEs will run those tests and you can lay down breakpoints to figure out why the routine is bombing or giving an incorrect result. When&#x2F;if something breaks and you find yourself debugging back into any particular nitty areas, that might indicate a good place to think about putting a test so you don&#x27;t have to exert yourself getting to that breakpoint again, next time you&#x27;d run the test and get a pass fail. I wouldn&#x27;t worry at all about the purity of it all, think about how having a test can save you time in the debugging process thats it.
bjourneover 2 years ago
I get what you mean with the tip of the iceberg analogy. The problem is that you can&#x27;t write any insightful tests before you have written the structure of the application and you can write the application before you have written the tests cause of TDD.<p>So what I do is that I first hack out the application before I write any tests. E.g if I code a matrix multiplication routine I&#x27;m not going to waste time writing unit tests for it. Cause perhaps a few hours later I might realize that I don&#x27;t need matrix multiplication after all. An added benefit is that I don&#x27;t have to constantly &quot;context switch&quot; between test writing mode and implementation writing mode.<p>I write tests when I have something interesting to test. So if the application unexpectedly crashes when I run it I write a test that triggers the crash. Then I fix the bug which causes the test to pass. I find these types of tests to be much more valuable than the &quot;cookie-cutter&quot; unit tests TDD expects you to write.
simonwover 2 years ago
I&#x27;ve found that strict write-the-tests-first TDD doesn&#x27;t work for me for most problems. The exception is things like &quot;parse specific components out of this string&quot; - really straight-forward this input should result in this output code, where sometimes I&#x27;ll write the tests first.<p>What DOES work for me is having a rule that any implementation should be accompanied by tests that prove the implementation works, bundled in the same commit.<p>I&#x27;ve been doing this habitually for a few years now and it has had a dramatic positive impact on my productivity.<p>I wrote a bit about how I do this here: <a href="https:&#x2F;&#x2F;simonwillison.net&#x2F;2020&#x2F;Feb&#x2F;11&#x2F;cheating-at-unit-tests-pytest-black&#x2F;" rel="nofollow">https:&#x2F;&#x2F;simonwillison.net&#x2F;2020&#x2F;Feb&#x2F;11&#x2F;cheating-at-unit-tests...</a>
pramodbiligiriover 2 years ago
If you take TDD literally you’ll end up writing the inner domain logic layers first purely because they’re easier to test using objects and APIs. For greenfield projects that’s a risk because you don’t know how that fits with the user&#x2F;usage side of things.<p>I’ve found it safer to start with (manual) integration tests where I verify intended behavior from outside in. That avoids building inner layers which turn out to be slightly but annoyingly off compared to what is desired.<p>Unfortunately, as soon as you jump off the pure OO bits and domain logic, the TDD-ness of your end-to-end program could be all over the place. You’re entirely at the mercy of available tools and your proficiency with them, and the time you can invest in learning those tools.
Waterluvianover 2 years ago
When I was newer I tried it as a hard rule and it sucked. So much wasted time and silly tests that didn’t test anything interesting.<p>Now I have a sixth sense for what I should write tests for and when I should write them before vs after the implementation.<p>Rules are misleading and teach the wrong thing. Instead, here’s one loose question I ask myself: “does the interface inform the implementation or the other way around?”<p>When the interface is super important and known, I can write a test first. When the implementation will reveal what the interface’s shape might be like, the test might come after.
nobodyandproudover 2 years ago
I don’t know if this is pure or orthodox but: Start with one or two high-level interfaces that should meet the functional requirements.<p>It defines the information you need to send in and&#x2F;or receive.<p>Non functional. Then capture some basic behavior of one of the methods&#x2F;properties in the tests, then implement the solution. Then start capturing edge-cases the same way.<p>If you’re asking yourself “how” or “should” I test this, then you’re doing great.<p>Apply SOLID principles to separate meaningful concerns into separate classes; and iterate there.<p>Take my viewpoint with a large grain of salt.
gijoeyguerraover 2 years ago
TDD is hard when you don&#x27;t know what your goal is. But it&#x27;s easy to &quot;just start executing code&quot;. I leverage it to get into an exploratory mode in order to think through my designs.<p>How I TDD 1. create a file with a test. 2. Get it running continuously (dotnet watch test or mocha --watch) 3. Write the code in the same file (just to start the design) as the test so I don&#x27;t have to bounce around 4. Iterate at step 3 for a while to explore the core domain&#x2F;model&#x2F;data (in your example, a Transaction has an amount, a payee, and time for instance), just to reach &quot;escape velocity&quot; 5. Ruthlessly push out dependencies that cross boundaries (file system, network, etc), protecting the core 6. Practice Red-Green-Refactor technique (write a failing&#x2F;not compiling test, make it pass, refactor) 7. Red-Green phase, I just make things work 8. Refactor phase, I design, reflect on object&#x2F;function communication patterns<p>Designing architecture is not easy. And keeping it simple, even less. So I&#x27;ve spent time learning about design patterns, boundaries, and communication patterns and implications to improve my design skills.
benstopicsover 2 years ago
I think you are doing it correctly, you just need to break that iceberg into smaller chunks. You’re getting caught up in the ideological nonsense that surrounds TDD and Scrum. “It can categorize a single transaction correctly” should be translated to a problem statement, “I want to be able to categorize a transaction.” You must then brainstorm feasible solutions for this or different aspects of the problem, develop the spec for implementing that, in the form of tests, “able to do this, able to do that,” and build the solution. Then afterwards you can test it against some examples and ask yourself, does this solve the problem? How to improve? Come up with a change or completely different solution, rinse, repeat. I would say a general statement like categorize a transaction is difficult because you don’t have any specific examples. Come up with a list of a dozen transactions that need categorizing and your TDD experience will become much less frustrating.
codaphiliacover 2 years ago
Don’t. Address the problem correctly. Write good integration tests. Spot the code with corner cases. unit tests these. avoid too much mocks. Docker is your friend
orwinover 2 years ago
There is a lot of really good advices here about when to do TDD and how to do it, so i would only reiterate earlier comments.<p>My personal experience is that i start TDD once my project is &quot;mature&quot; enough that the next upgrade&#x2F;addition can be put at two different place easily enough and i don&#x27;t know where is the best place to do it. Basically, on my latest project, i asked myself something like: &quot;is this part of this class method, or should the main loop take care of it?&quot;. That&#x27;s where i started completing tests to 90% coverage (main loop + pagination mocks were skipped, ideally i could&#x27;ve reach 95% but laziness caught up) and started TDD. In the end, i still made the wrong choice and added my change to the class method, only to change it back two weeks ago (or roughly six month later). Here, TDD didn&#x27;t help me to make the best software architecture choice, but having to write test first for this part of code made me decouple it from the method i originally put it in, and allowed me to made a architecture change in less than an hour.
ajmurmannover 2 years ago
I still recommend to stick with outside-in TDD. It will help you discover interfaces and not over-engineer them. However, don&#x27;t get too hung up on fulfilling that outermost feedback loop and test immediately. It&#x27;s OK to spend a lot of time on one of the inner components and commit that and its tests before moving on to the next piece you need to get the outermost test to pass. In fact, it&#x27;s a great way to make iterative, committable progress towards the larger goal. I know it can feel weird to not get back to the outermost test for a long while, but that just means that your user intercase is simple, but a lot of work is behind the scenes. Nothing wrong with that.<p>Another piece of general advise on TDD: Adjust what tests and how many you write based on what your language already gives you. E.g. when I write Ruby or JS, I&#x27;ll write a ton of unit tests for almost Avery conceivable scenario. When writing Rust or Swift I write mostly unit tests for interesting business logs, but not much else.
rozenmdover 2 years ago
You don&#x27;t.<p>If it doesn&#x27;t fit your mental model of programming, just don&#x27;t use it.
CharlieDigitalover 2 years ago
&gt; I know I need to, at least, read a CSV, parse a CSV row into some data structure and then categorise it. So I TDDed my way through to complete these “sub problems”. However, when I was about to apply the categorisation rules I realised that the data structure I created didn’t help, was just bad and didn’t work at all<p>TDD is kind of a forcing function for modular design (whether OOP or functional programming).<p>In your scenario, you&#x27;d want:<p>1) a module to read the CSV -&gt; test that it can load a file correctly given a path string, correctly handles invalid paths (format, file does not exist, etc)<p>2) a module to parse a row -&gt; given a string, test that it can parse a 0 row file, a 1 row file, a 2 row file, and correctly handles an unparseable file (e.g. binary file); test that it correctly handles incorrectly formatted rows<p>3) a module to categorize -&gt; given a set of rows, test that it can categorize a sample set correctly with expected output<p>If you try to make 2 depend on 1 or 3 depend on 2 and 1, it&#x27;s no longer a unit test and becomes more of an integration test.
评论 #32714104 未加载
JakeAlover 2 years ago
This is how I started going about managing complex design processes, BEFORE agile and whatever buzzwords they use to describe the same problem discovery, identification, positioning ad solution proposals. You go as deep as you need to THINK through the details involving all people who know the details. When you do it right all of the correct people fully inform the process and any TDD that is derived from it, all while keeping everyone in consensus about the goal and the decisions being made to get there. But it can all be worked on paper first if you don&#x27;t cut corners and have the right people involved (like GUI designers and not marketing or the client should be designing interfaces).<p><a href="http:&#x2F;&#x2F;www.dubberly.com&#x2F;articles&#x2F;managing-complex-design-projects.html" rel="nofollow">http:&#x2F;&#x2F;www.dubberly.com&#x2F;articles&#x2F;managing-complex-design-pro...</a>
karmakazeover 2 years ago
For me, I usually have an idea for an overall simple design or approach that will likely work. If I&#x27;m unsure, I might post an issue that describes the theory of operation for feedback or iteration. TDD is the rest of what follows.<p>Now what does work is if you can decompose the whole idea into parts, each of which is written using TDD. You might still want to outline some key interfaces so you get the shapes of things to fit. With practice, all this extra stuff often happens in your head as you&#x27;re doing TDD and you make adjustments along the way.<p>Also, the way you described making bottom-up things that didn&#x27;t quite work, is a valid and possibly effective way of doing TDD. If you&#x27;re able to determine that a lower implementation is unsuitable it can be rewritten and if the tests are not too brittle will give you confidence to do the rewrite safely and faster.
teddyhover 2 years ago
&gt; <i>Before that one, I tried the opposite: a more “bottom up” approach. I know I need to, at least, read a CSV, parse a CSV row into some data structure and then categorise it. So I TDDed my way through to complete these “sub problems”. However, when I was about to apply the categorisation rules I realised that the data structure I created didn’t help, was just bad and didn’t work at all :).</i><p>I think what would have worked for you is to write a test for the categorization, but feed it pre-parsed data structures in the tests. If the data structure is wrong, you will find out now. After that works, you have decided on a correct data structure, and you now can write code to read and parse CSV into those same data structures.<p>It’s all a matter of practice. If you do things in the incorrect order, these things can happen.
ArrayBoundCheckover 2 years ago
First you need to be experienced enough to know what not to do. Like that you shouldn&#x27;t write mocks, shouldn&#x27;t test private functions, shouldn&#x27;t use a ton of global or static vars etc<p>Then you should find some sort of coverage tool so you can know if you covered most lines or not. I tend to ignore error handling lines but if its easy to get into that case I&#x27;ll writ a test for it. Then I&#x27;ll write test that take &lt;1ms to execute. If something needs a network I&#x27;ll try to separate the logic so it can work on a file OR I&#x27;ll it connect to something on my local machine.<p>Generally I try to get 95%+ coverage (including error cases). I have many test and the entire suite run in &lt;100ms. Maybe my project is easier to do TDD on but that&#x27;s the way I do it
honkycatover 2 years ago
You are allowed to &quot;spike&quot; out a messy implementation before your actual implementation to see what the actual shape of the code will look like. From there you re-implement it using proper TDD.<p>Don&#x27;t test private functions. Only test the public interface.<p>Use dependency injection everywhere.
tpoacherover 2 years ago
I think part of it, by reading your question, is that:<p>a) TDD primarily refers to unit tests (it does not address other types of testing that you need to take into account when designing a program)<p>b) you have the wrong idea of what constitutes a &#x27;unit&#x27; test versus other kinds of testing. Specifically, as per M. Feathers, anything that touches the filesystem, e.g. to read a file or database (as you do here), is not a unit test.<p>I would highly recommend Chapter 2 of M. Feathers&#x27; &quot;Working effectively with Legacy Code&quot; on this topic.
fedeb95over 2 years ago
Read about AMDD. The idea is: you not only write tests to design and iterate on that, but also sketch a model of your system and iterate that. Depending on the size you may want to sketch subsystems, etc.<p>Now the only problem I have with TDD is time. It takes a lot of time to do it right and usually I don&#x27;t have that time (in my organisation). So maybe you can tdd some parts of your application, maybe the most critical parts. Even for personal projects you may not have the time: demotivation usually kicks in for personal projects. TDD defers results and the positive feedback needed to keep going. At least in my experience
评论 #32719192 未加载
评论 #32716843 未加载
ahurmazdaover 2 years ago
Don’t have a solution to learning best practices other than watching good practitioners at work.<p>However, a big inflection point for me was getting hold of a good&#x2F;capable IDE (intellij). It removes a lot of the drudgery.<p>You write test with yet-to-be defined functions. IDE let’s you run the single test (amongst many) with one shortcut. Fails ofc!. Then you use a shortcut to let IDE implement the function (takes you where it needs to be implemented, easy with python modules eg. YMMV). Rinse and repeat.
thihtover 2 years ago
I use TDD only when fixing reported bugs. First I write the test for the expected result, see that it fails, fix the code and check that the new test passes.<p>TDD as it’s supposed to be done according to evangelists doesn’t work. I have the same problem as you every time I try TDD for implementation, and the answer I get is always that I’m doing it wrong somehow. I have better results without TDD in these cases though.
nrrover 2 years ago
In short: You don&#x27;t. At least, not in quite the way that the orthodox expansion of TDD implies.<p>I&#x27;ve found that TDD with a reading of &quot;Testability-Driven Design&quot; was useful for driving adoption of certain architectural patterns that made my software more easily testable. That, in turn, informed how I authored my APIs. And so on.
abraxasover 2 years ago
Most software development these days consists of applying glue code to myriad of frameworks and libraries as opposed to writing algorithmic code. This kind of work does not lend itself well to TDD.
评论 #32713767 未加载
评论 #32714728 未加载
plutarover 2 years ago
I find it hard to implement at the beginning of a project but I find it very easy to implement once I find a bug. Fixing bugs by starting with a failed test make it easy imo.
mbrodersenover 2 years ago
TDD doesn’t work for me. However having a large number of system&#x2F;use case tests works wonders. I haven’t had a bug in production for 5+ years.
stitched2gethrover 2 years ago
For me, TDD has been much more about making sure the code works the first time and to the spec and much less about the structure of it.
norwalkbearover 2 years ago
Tdd only works when you have extremely clear biz requirements like in many big companies.
julienreszkaover 2 years ago
There are tons of books about the subject. Can tell us which you already read?
评论 #32715665 未加载
hbrnover 2 years ago
My guess is that TDD probably works well enough for external consultants or outsourcers, where you have to come up with <i>really</i> good requirements in advance. That said, if you have really good requirements, any methodology will work.<p>A funny pattern I noticed is most TDD debates is that whoever challenges TDD always comes up with concrete examples, and TDD advocates always stay incredibly vague.<p>There&#x27;s a good discussion between DHH and Martin Fowler&#x2F;Kent Beck on TDD: <a href="https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;is-tdd-dead&#x2F;" rel="nofollow">https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;is-tdd-dead&#x2F;</a><p>You can also look up Ron Jeffries and Peter Norvig attempts at sudoku solvers. Guess who actually solved the problem, and who wrote 5 blogs posts without getting anywhere?<p>TDD is snake oil.
UK-ALover 2 years ago
Quite frankly very few people can do TDD well. Hence why most developers hate it. They&#x27;ve never been taught by a master.<p>The majority of developers have less than 5 years experience, and they&#x27;re teaching new devs. So the skills that we&#x27;re developed by people who embraced TDD in the early 2000s have been swamped out by new people.<p>I think the best way learn if you don&#x27;t have access to a master is probably videos. See if can find kent beck doing some examples. There&#x27;s subtle knowledge &amp; flows that hard to teach through just text alone.
KronisLVover 2 years ago
&gt; The problem with this is that this is testing the “tip of the iceberg” that will be almost the whole, final app.<p>This is also what I&#x27;ve run into in practice, across numerous systems.<p>Actually the only cases where I&#x27;ve been able to do TDD effectively (and get &gt;95% test coverage) was when I was in control over the entire codebase and its design from day one, thus being able to enforce whatever modularization was necessary by the tests, otherwise the systems quickly tended to get untestable (at least in regards to being able to start with a test). In addition, I was generally more successful in cases where I was dealing with writing libraries (that are called by other code), as opposed to web applications which need to deal with shuffling around bunches of data.<p>Let me give you a few examples of what didn&#x27;t work. Suppose that I&#x27;m asked to help out with this pre-existing codebase that&#x27;s been around for 5 years or so. I look how some request to purchase a product is processed (just a made up example, though along the lines of what I&#x27;ve seen): first you have some server side rendering with the full complexity of PrimeFaces and around 10-20 variables that affect whether certain buttons are visible, possibly there being a modal dialog or redirection in the middle of the process.<p>Then, you have the view code, which once again contains different methods that interact with services, passing data around and choosing whether to display additional messages or something else. Then, in the service layer, you might have 5-10 services that are involved in retrieving the data necessary for everything, from auditing and e-mail notifications, getting various pieces of information about users&#x2F;billing&#x2F;discounts and then checking all of that against what the user actually wants to purchase, perhaps some validation code thrown in along the way.<p>Validation code which also might need to interact with the database due to incrementally grown business requirements over the years. And from there on out, additional code for returning early in the case of any errors being present, which might need to be further processed for displaying them to the user. Oh, and if the process is successful without any errors, then you might also have some scheduled processes that would be created and would need to be handled. Almost any of these steps could have transaction rollbacks along the way and there might also be a large amount of PrimeFaces&#x2F;Spring related code and workarounds in there.<p>And I&#x27;ve seen similar patterns across many projects, almost regardless of technologies used (Java is just a good example): if you give developers the chance to create tightly coupled code, they will jump at it horrifyingly often, especially if they&#x27;re under time pressure or have a &quot;good enough&quot; mentality. Over time, any sorts of boundaries will dissolve, unless you go for a microservices based approach, but even then you&#x27;ll still be dealing with &quot;magic&quot; code which will be hard to test (especially if you need some kind of application context to be running during the tests), be it IoC&#x2F;DI, annotations&#x2F;decorators, proxies for your code, or even something like ORM interactions.<p>Speaking of databases, good luck if your code has like 10-20 database calls along the hot path, which you&#x27;ll need to mock or actually let it hit some database instance, though hopefully one with state that&#x27;s either reproducible or can be rolled back (both of which will make your CI slow and more error prone). You might go for an in-memory database, but good luck trying to transpose or make Oracle-specific complex queries portable to something like that, given that for many learning JPQL is too much.<p>In short: there are situations where TDD is simply unsuitable because of how certain systems&#x2F;domains are architected (I&#x27;m yet to see the PrimeFaces layer as something easily testable) and you&#x27;re better off simply using unit tests where you can, as well as a healthy helping of integration tests (which you may or may not be able to do, depending on how your environments are laid out).<p>Of course, things might not look so dire for projects that are a bit newer and where you can still alter their structure. For library code, however, personally I&#x27;d recommend TDD, especially if you&#x27;re ever in the need of doing anything security adjacent (e.g. wrapper code around proven security libraries). And for basically all of the cases where you can write mostly functional code with little to no side effects.<p>Also, here&#x27;s a not so fun memory: trying to write tests that dealt with loading data from the file system. In Java, you can have a common piece of code for doing that (e.g. Paths.get), though what paths are valid will depend on the actual system and file system that JVM will be running on. In other words, when locally you have Windows and the CI server has a Linux distro or something, your tests will not run consistently. But you don&#x27;t want to test that bit of code? Better be prepared to add it as an exception to any code coverage quality gate that you&#x27;ve set up. So annoying.
JohnHaugelandover 2 years ago
Just chase coverage and disallow frivolous tests. It&#x27;s really that simple.
Jtsummersover 2 years ago
I&#x27;m not quite getting your categorization of the first approach as top-down and second as bottom-up, they are the opposite to me.<p>Your program is going to have a flow like this:<p><pre><code> $ rcd2-awesome-finance-app september2022.csv Category Amount Food $100.00 Clothes $200.00 ... </code></pre> Internally it&#x27;ll be like this:<p><pre><code> main(string filename) csv = open(filename) cat-&gt;$ = new dict() foreach line in csv transaction = parse(line) cat = categorize(transaction) amount = amount(transaction) &#x2F;&#x2F; may just be t[2] not a function cat-&gt;$[cat] = cat-&gt;$[cat].or_default(0) + amount &#x2F;&#x2F; or other logic for first time seeing it foreach (cat, amount) in cat-&gt;$ print(&quot;{cat}\t{amount}\n&quot;) </code></pre> So your second example is the top-down one, it requires all this logic to exist. That makes TDD harder because going from a one-line CSV file and hardcoding the amount and category (first code is dumb code), to a second one-line CSV and actually categorizing is a massive jump.<p>Categorizing is the core, so categorizing is the thing that really needs testing. The rest doesn&#x27;t even need (substantial) testing anyways, it&#x27;s a bog standard CSV-data driven tool. Which leads to a point I want to make:<p>You don&#x27;t need to do TDD on everything to do TDD. TDD is the &quot;show your steps&quot; of programming (analogous to the high school algebra requirement that you show and name each step in a solution). Many people despise that in their math homework, but it&#x27;s important for the teacher for the same reason TDD can be important to you, the programmer.<p>Math teachers (ok, some may, they&#x27;re poor teachers) don&#x27;t ask students to show their steps just for grins. They ask it so they can observe the thought process of the student. A student tasked with solving for x with `2x = 4` may come up with `x=2`, but how? Did they divide by 2 or subtract 2 (I&#x27;ve done my share of math tutoring, I have seen students subtract what should&#x27;ve been divided). They got the answer by coincidence. So when they are given another problem where that coincidence doesn&#x27;t work, they get the wrong answer and in more complex problems <i>how</i> they went wrong is non-obvious ( in 3x=9 =&gt; x = 6 it would be glaringly obvious, but with more terms and unobserved steps it&#x27;s not; it&#x27;s early the coffee hasn&#x27;t kicked in yet so I can&#x27;t think of good example problems to illustrate this).<p>TDD does the same thing, but it&#x27;s there for you, not an instructor, to observe what you are thinking along the way. Each test is supposed to be small so that you can easily see that the logic is correct. However, if you can see a larger step to take (like that structure I threw up at the top or parts of it) then go for it. I&#x27;ve written I don&#x27;t know how many CLI apps and I would never use TDD for anything but that inner categorization logic (or its equivalent for them). It would be silly for me to write a test to see that I was reading the file correctly. I can think of only one reason to do it: You don&#x27;t know the language and API yet so you want to test your own knowledge, more than the program itself, to ensure you&#x27;re calling it correctly. Even then, unless it has a weird file reading API I wouldn&#x27;t bother, 99% of them will be the same or similar enough.
评论 #32716860 未加载