It’s definitely great in theory until you inherit a codebase with no tests, poor documentation, and numerous reported bugs already live in production. Even better if it was written by people hired because they could do other things better than they could code - which looking at some of the unlabeled wiring messes we were left, isn’t saying a lot.
It’s criminally underutilized. Of course, one reason is that it’s hard to TDD a moving target. Since it’s also hard to get people to actually fucking specify things in a lot of real world cases, it’s just one more thing you ought to do, but aren’t allowed to.
I think you have a point with the moving target, but also I believe that development should pretty much always be a moving target. You should be refactoring your domain based on new experiences and new knowledge all the time. So, personally, I find integration tests much more useful, because they test the input and output of a system, rather than how it’s implemented. I can change my domain without having to modify my tests and that makes changes to the domain much simpler.
That being said, I also definitely recognize the advantages of TDD, I just don’t think it’s a silver bullet; there’s good projects for it and bad ones
I’ve worked with projects that does the right thing, but no one can add new features to it because it’s a nightmare to work with. It’s at the level of not being able to move a button on the UI without breaking how the software interacts with the cloud.
TDD is great when you have a very narrow use case, for example an algorithm. Where you already know beforehand: If I throw A in B should come out. If I throw B in C should come out. If I throw Z in an error should be thrown. And so on.
For that it’s awesome, which is mostly algorithms.
In real CRUD apps though? You have to write the actual implementation before the tests. Because in the tests you have to mock all the dependencies you used. Come up with fake test data. Mock functions from other classes you aren’t currently testing and so on. You could try TDD for this, but then you probably spend ten times longer writing and re-writing tests :-/
After a while it boils down to: Small unit tests where they make sense. Then system wide integration tests for complex use-cases.
Yeah, I’m constantly recommending junior devs to use TDD specifically for this. I don’t recommend it for anything else. If they don’t write the test first, it’s possible that the test will end up testing the wrong thing and thus they can’t be sure they really did fix the bug.
Sometimes it’s hard to tell where to write the test ahead of time, so sometimes a slight variation I do is to write the test after (usually because it was such a struggle to figure out where the bug is), but when I’m testing it, I’ll comment out the fix or whatever and make sure the test fails.
I mean, it sounds more like “The messier your project, the more difficult the unit testing”. What you’re describing sounds like issues with SRP and LoD. Which will inevitably happen as big projects get rushed, but let’s place the blame where it belongs: rushing.
Yes unit tests take longer up front, but for projects that you need to update and maintain for a long time, they’re a huge boon.
You can’t do everything with a unit test obviously.
I think we should all strive to do better. Unit tests, mock-ups, UX design, 2 week sprints with actual working deliverables, well documented use cases, every thing neatly stacked in Jira, dev,test,staging,prod environments, continuous integration and every thing else we are told to do.
Then reality sets in……
With all that said, 25 years as a dev, this utopian environment is almost impossible to find unless forced by regulatory compliance. Medical devices, life critical systems, etc. or if you have big piles of money.
In my experience, those things tend to be forced by project managers who believe the highest law of the land is proper scrum. Unsurprisingly, this makes all the devs miserable with no way to change anything because “this is just how it’s done”.
then you probably spend ten times longer writing and re-writing tests
This is always what I’ve seen personally when people use TDD. And it’s worse because the inevitable time crunch towards the end of the project means the developers stop maintaining the tests, which renders all of the work put into the tests up to that point useless.
With my stakeholders TDD is nearly impossible. I mean it’s possible, but doesn’t make sense as they shuffle their specifications every other day. I implement, they decide they wanted something different, I refactor, they don’t like it, I refactor, they accept, I write tests.
Unfortunately most product managers SUCK at designing or making software.
Agile tries to fix this be supporting frequent iteration.
Unfortunately most programmers SUCK at writing good code.
TDD tries to fix this by forcing the consideration of end results (testing) at the beginning. It forces programmers and product teams to actually think and work. Make clear design decisions earlier on, but not to the point of waterfall.
It’s just a giant cesspool of failure due to human laziness that usually falls on the shoulders of QA.
Bottom line, making good software is hard. It takes time. But the market won’t support slow development. The business and sales teams remind me of Veruca Salt in Willy Wonka.
Exactly, from my experience, most of the time (primarily when I need to do something new) I start writing code, when it starts working then I am starting to refractor it so it doesn’t look like crap.
Perhaps TDD would make sense, when before any actual work starts, we would have POC phase to understand what needs to be done.
I do it sometimes, especially when the bug is hard to reproduce and I know exactly what’s causing it. Sometimes it’s quicker to write the tests than to test manually.
I like TDD in theory and I spent so many years trying to get it perfect. I remember going to a conference where someone was teaching TDD while writing tic tac toe. Unsurprisingly, he didn’t finish in time.
The thing that I hate is people conflating TDD with testing or unit testing. They’re vastly different things. Also, I hate mocks. I spent so long learning all the test doubles to pass interviews: what’s the difference between a spy, fake, stub, mock, etc. Also doing it with dependency injection and all that. I much prefer having an in-memory database than mock what a database does. Last company I worked at, I saw people write tests for what would happen if the API returned a 404 and they wrote code that would handle it and all that. In practice, our HTTP library would throw an exception not return with a statusCode of 404. Kinda funny.
You obviously can’t always get replacements for things and you’ll need to mock and I get that. I just prefer to not use them if I can.
Also, TDD advocates love saying, you’re just not doing it well or you just don’t know enough.
I get it, you love TDD and it works for you and more power to you.
I definitely believe in testing and having resilient tests that will minimize changes upon refactoring, but TDD doesn’t work for me for most of the work I do. It works for some and I love it when it does, but yeah … sorry random long ramble.
I much prefer having an in-memory database than mock what a database does.
Which sounds great in theory but then you get to find where your prod DB and testing DB differ and you have to keep chasing that. Unless you are using something like SQLite which has both (disk and in-memory) as an option.
I worked at a place that used a different in-memory DB (H2, IIRC) in place of our MySQL DB for testing. It ended up being hell to maintain and had to have hacks for how H2 and MySQL differ (tests would work in H2 but fail if run against MySQL or vice versa).
I had a coworker who was big into TDD. He was using it on a disaster project that was way over budget and long overdue. I was sitting in on a meeting between him and the client when he tried to defend the project’s status by saying “you don’t understand - we’ve written six times as much test code as actual code!” The client almost punched him.
IMO it doesn’t matter what methodology you use if a) you don’t have the ability to understand what the client actually needs, and 2) you can’t code your way out of a paper bag (or to put it more technically, if you over-architect your solution and then can’t solve all the self-inflicted problems you run into).
Time spent on tests is time saved in debugging, firefighting, troubleshooting, etc. If the project breaks down with a simple change, then tests also save the sanity of developers, and allows them to refactor the architecture.
a) you don’t have the ability to understand what the client actually needs
the client doesn’t understand either. This I have had to learn to accept and not blame the client for, it’s OK and we’ll figure it out together
b) if you over-architect your solution
we can’t figure out what we actually need by overarchitecting something to death. If and when you find you’ve coded yourself into a corner because you didn’t architect well enough 6 months ago, then congratulations it seems like what you’re doing is good because you’ve made enough progress to actually need a better architecture
obviously I’m oversimplifying and people more experienced than me understand better how to walk the tightrope between unmaintainable spaghetti and an overengineered mess, but me, I try to keep shit as simple as possible because you never know
Also, I hate mocks. I spent so long learning all the test doubles to pass interviews: what’s the difference between a spy, fake, stub, mock, etc.
Oh good grief. I haven’t even heard of half of those, and I’ve been writing code longer than most interviewers.
Also doing it with dependency injection and all that.
After struggling with several DI frameworks, I’ve come to the conclusion that DI is far too opaque and unpredictable to be useful.
OOP is also intentionally opaque, mind you, but debuggers can see through it if necessary, so that’s fine. DI, on the other hand, is opaque even to debuggers!
I much prefer having an in-memory database than mock what a database does.
I much prefer using the same DBMS for tests as the one used in production. That way, I can use its special features (like arrays as query parameters), and avoid bugs resulting from differences between DBMSes (which would only appear in production).
You obviously can’t always get replacements for things and you’ll need to mock and I get that. I just prefer to not use them if I can.
Indeed, and you usually can. Even if your system involves a bunch of services that normally run on different machines, who says you can’t spin up instances of them as part of the test? Tests are arbitrary code and can do anything that any other program can do, including starting subprocesses. Just be sure to terminate them once the test is finished, even if it fails.
After many failed attempts at TDD, I realized/settled on test driven design, which is as simple as making sure what you’re writing can be tested. I don’t see writing the test first as a must, only good to have, but testable code is definitely a must.
This approach is so much easier and useful in real situations, which is anything more complicated than foo/bar. Most of the time, just asking an engineer how they plan to test it will make all the difference. I don’t have to enforce my preference on anyone. I’m not restricting the team. I’m not creating a knowledge vacuum where only the seniors know how yo code and the juniors feel like they know nothing.
Just think how you plan to test it, anyone can do that.
This reminds me when a senior engineer asked me to write exception handling on a one-off python script, not a production code - just a script devs can use internally. The “handling” was that the program should exit when a file is not found. He wanted me to try the file open, except the file error, print “file not found” message and exit(1).
Guess what, genius. Python already does that for you. No need to write an extra wrapper needlessly.
I like to mock up dependencies with Docker Compose, then run all the tests against that. Keep the compose file in the repo, of course. I don’t tend to build a lot of real unit tests unless I’m doing something very novel and self contained. When you’re just assembling a service out of REST libraries and databases, integration testing is mostly what you want.
You are not logged in. However you can subscribe from another Fediverse account, for example Lemmy or Mastodon. To do this, paste the following into the search field of your instance: !programmerhumor@lemmy.ml
Post funny things about programming here! (Or just rant about your favourite programming language.)
Rules:
Posts must be relevant to programming, programmers, or computer science.
No NSFW content.
Jokes must be in good taste. No hate speech, bigotry, etc.
TDD is actually helpful once you get the hang of it.
Yeah but getting the hang of it is the hard part. I don’t know any dev in my company or my circle that uses it; we all did learn about it alright.
bad habits are hard to break.
It is a lifesaver in some cases
It’s definitely great in theory until you inherit a codebase with no tests, poor documentation, and numerous reported bugs already live in production. Even better if it was written by people hired because they could do other things better than they could code - which looking at some of the unlabeled wiring messes we were left, isn’t saying a lot.
Good way to figure out how an unknown code base works is to add unit tests tho
It’s also the only way to migrate architecture safely.
How does that even correlated?
It’s criminally underutilized. Of course, one reason is that it’s hard to TDD a moving target. Since it’s also hard to get people to actually fucking specify things in a lot of real world cases, it’s just one more thing you ought to do, but aren’t allowed to.
I think you have a point with the moving target, but also I believe that development should pretty much always be a moving target. You should be refactoring your domain based on new experiences and new knowledge all the time. So, personally, I find integration tests much more useful, because they test the input and output of a system, rather than how it’s implemented. I can change my domain without having to modify my tests and that makes changes to the domain much simpler.
That being said, I also definitely recognize the advantages of TDD, I just don’t think it’s a silver bullet; there’s good projects for it and bad ones
Years of experience speaking:
If your end results are following this pattern, no one gives a fuck how you do
Make it maintainable should be up there too.
I’ve worked with projects that does the right thing, but no one can add new features to it because it’s a nightmare to work with. It’s at the level of not being able to move a button on the UI without breaking how the software interacts with the cloud.
Jesus Christ
Add make it cheap and you got yourself a deal
Also make it yesterday.
Pick two.
Nah, do all three. That should have been a numbered list.
You’re goddamn right.
TDD is great when you have a very narrow use case, for example an algorithm. Where you already know beforehand: If I throw A in B should come out. If I throw B in C should come out. If I throw Z in an error should be thrown. And so on.
For that it’s awesome, which is mostly algorithms.
In real CRUD apps though? You have to write the actual implementation before the tests. Because in the tests you have to mock all the dependencies you used. Come up with fake test data. Mock functions from other classes you aren’t currently testing and so on. You could try TDD for this, but then you probably spend ten times longer writing and re-writing tests :-/
After a while it boils down to: Small unit tests where they make sense. Then system wide integration tests for complex use-cases.
I always start with the basics for my test cases. Like, every test case has a name, and
assert(true)
in the body.It’s also great for bug fixes. Write that sucker first and you have an easy way to reproduce the issue and check whether it’s fixed.
Yeah, I’m constantly recommending junior devs to use TDD specifically for this. I don’t recommend it for anything else. If they don’t write the test first, it’s possible that the test will end up testing the wrong thing and thus they can’t be sure they really did fix the bug.
Sometimes it’s hard to tell where to write the test ahead of time, so sometimes a slight variation I do is to write the test after (usually because it was such a struggle to figure out where the bug is), but when I’m testing it, I’ll comment out the fix or whatever and make sure the test fails.
I mean, it sounds more like “The messier your project, the more difficult the unit testing”. What you’re describing sounds like issues with SRP and LoD. Which will inevitably happen as big projects get rushed, but let’s place the blame where it belongs: rushing.
Yes unit tests take longer up front, but for projects that you need to update and maintain for a long time, they’re a huge boon.
You can’t do everything with a unit test obviously.
Totally agree.
I think we should all strive to do better. Unit tests, mock-ups, UX design, 2 week sprints with actual working deliverables, well documented use cases, every thing neatly stacked in Jira, dev,test,staging,prod environments, continuous integration and every thing else we are told to do.
Then reality sets in……
With all that said, 25 years as a dev, this utopian environment is almost impossible to find unless forced by regulatory compliance. Medical devices, life critical systems, etc. or if you have big piles of money.
In my experience, those things tend to be forced by project managers who believe the highest law of the land is proper scrum. Unsurprisingly, this makes all the devs miserable with no way to change anything because “this is just how it’s done”.
Hahaha. Yea. Been there too.
Well put.
This is always what I’ve seen personally when people use TDD. And it’s worse because the inevitable time crunch towards the end of the project means the developers stop maintaining the tests, which renders all of the work put into the tests up to that point useless.
That’s not a problem with unit tests, that’s a problem with project management
With my stakeholders TDD is nearly impossible. I mean it’s possible, but doesn’t make sense as they shuffle their specifications every other day. I implement, they decide they wanted something different, I refactor, they don’t like it, I refactor, they accept, I write tests.
Please send help
Send help? We’re all caught in the same fire.
I found a solution: I quit coding and became a school bus driver.
deleted by creator
I feel you, man. 🍻
Yep. I’ve been there.
Sounds like you either need to establish better task parameters or you need to gain more experience programming.
This is very true.
Unfortunately most product managers SUCK at designing or making software.
Agile tries to fix this be supporting frequent iteration.
Unfortunately most programmers SUCK at writing good code.
TDD tries to fix this by forcing the consideration of end results (testing) at the beginning. It forces programmers and product teams to actually think and work. Make clear design decisions earlier on, but not to the point of waterfall.
It’s just a giant cesspool of failure due to human laziness that usually falls on the shoulders of QA.
Bottom line, making good software is hard. It takes time. But the market won’t support slow development. The business and sales teams remind me of Veruca Salt in Willy Wonka.
GIF
Works great when you know what your doing before you start. That never actually happens in real life though.
Exactly, from my experience, most of the time (primarily when I need to do something new) I start writing code, when it starts working then I am starting to refractor it so it doesn’t look like crap.
Perhaps TDD would make sense, when before any actual work starts, we would have POC phase to understand what needs to be done.
And often if you box yourself into an API before you start implementing, it comes out worse.
I always learn a lot about the problem space once I start coding, and use that knowledge to refine the API of my system as I work.
I do it sometimes, especially when the bug is hard to reproduce and I know exactly what’s causing it. Sometimes it’s quicker to write the tests than to test manually.
TDD is up there with CBT if no one knows how to implement it properly
Casino Royale had a nice demonstration.
Cock and Ball torture simply must be done right
I like TDD in theory and I spent so many years trying to get it perfect. I remember going to a conference where someone was teaching TDD while writing tic tac toe. Unsurprisingly, he didn’t finish in time.
The thing that I hate is people conflating TDD with testing or unit testing. They’re vastly different things. Also, I hate mocks. I spent so long learning all the test doubles to pass interviews: what’s the difference between a spy, fake, stub, mock, etc. Also doing it with dependency injection and all that. I much prefer having an in-memory database than mock what a database does. Last company I worked at, I saw people write tests for what would happen if the API returned a 404 and they wrote code that would handle it and all that. In practice, our HTTP library would throw an exception not return with a statusCode of 404. Kinda funny.
You obviously can’t always get replacements for things and you’ll need to mock and I get that. I just prefer to not use them if I can.
Also, TDD advocates love saying, you’re just not doing it well or you just don’t know enough.
I get it, you love TDD and it works for you and more power to you.
I definitely believe in testing and having resilient tests that will minimize changes upon refactoring, but TDD doesn’t work for me for most of the work I do. It works for some and I love it when it does, but yeah … sorry random long ramble.
Which sounds great in theory but then you get to find where your prod DB and testing DB differ and you have to keep chasing that. Unless you are using something like SQLite which has both (disk and in-memory) as an option.
I worked at a place that used a different in-memory DB (H2, IIRC) in place of our MySQL DB for testing. It ended up being hell to maintain and had to have hacks for how H2 and MySQL differ (tests would work in H2 but fail if run against MySQL or vice versa).
I had a coworker who was big into TDD. He was using it on a disaster project that was way over budget and long overdue. I was sitting in on a meeting between him and the client when he tried to defend the project’s status by saying “you don’t understand - we’ve written six times as much test code as actual code!” The client almost punched him.
IMO it doesn’t matter what methodology you use if a) you don’t have the ability to understand what the client actually needs, and 2) you can’t code your way out of a paper bag (or to put it more technically, if you over-architect your solution and then can’t solve all the self-inflicted problems you run into).
Time spent on tests is time saved in debugging, firefighting, troubleshooting, etc. If the project breaks down with a simple change, then tests also save the sanity of developers, and allows them to refactor the architecture.
the client doesn’t understand either. This I have had to learn to accept and not blame the client for, it’s OK and we’ll figure it out together
we can’t figure out what we actually need by overarchitecting something to death. If and when you find you’ve coded yourself into a corner because you didn’t architect well enough 6 months ago, then congratulations it seems like what you’re doing is good because you’ve made enough progress to actually need a better architecture
obviously I’m oversimplifying and people more experienced than me understand better how to walk the tightrope between unmaintainable spaghetti and an overengineered mess, but me, I try to keep shit as simple as possible because you never know
Oh good grief. I haven’t even heard of half of those, and I’ve been writing code longer than most interviewers.
After struggling with several DI frameworks, I’ve come to the conclusion that DI is far too opaque and unpredictable to be useful.
OOP is also intentionally opaque, mind you, but debuggers can see through it if necessary, so that’s fine. DI, on the other hand, is opaque even to debuggers!
I much prefer using the same DBMS for tests as the one used in production. That way, I can use its special features (like arrays as query parameters), and avoid bugs resulting from differences between DBMSes (which would only appear in production).
Indeed, and you usually can. Even if your system involves a bunch of services that normally run on different machines, who says you can’t spin up instances of them as part of the test? Tests are arbitrary code and can do anything that any other program can do, including starting subprocesses. Just be sure to terminate them once the test is finished, even if it fails.
After many failed attempts at TDD, I realized/settled on test driven design, which is as simple as making sure what you’re writing can be tested. I don’t see writing the test first as a must, only good to have, but testable code is definitely a must.
This approach is so much easier and useful in real situations, which is anything more complicated than foo/bar. Most of the time, just asking an engineer how they plan to test it will make all the difference. I don’t have to enforce my preference on anyone. I’m not restricting the team. I’m not creating a knowledge vacuum where only the seniors know how yo code and the juniors feel like they know nothing.
Just think how you plan to test it, anyone can do that.
This reminds me when a senior engineer asked me to write exception handling on a one-off python script, not a production code - just a script devs can use internally. The “handling” was that the program should exit when a file is not found. He wanted me to
try
the file open,except
the file error, print “file not found” message and exit(1).Guess what, genius. Python already does that for you. No need to write an extra wrapper needlessly.
I like to mock up dependencies with Docker Compose, then run all the tests against that. Keep the compose file in the repo, of course. I don’t tend to build a lot of real unit tests unless I’m doing something very novel and self contained. When you’re just assembling a service out of REST libraries and databases, integration testing is mostly what you want.
I actually love using TDD in my real life development job…
Elaborate.