What is a good introduction to test-driven development (TDD)?
I know about unit tests, integration tests, TDD, tried them once, but never really got it to stick. I see the value (necessity) of testing and would like to make it a habit.
What is a good introduction to test-driven development (TDD)?
I know about unit tests, integration tests, TDD, tried them once, but never really got it to stick. I see the value (necessity) of testing and would like to make it a habit.
@wizzwizz4 I heard there is a "cult" around TDD. I guess one can also over-do TDD, like e.g. over-engineer thinigs. I suppose in the end "the right tool for the right job" applies here again.
@floppy TDD is βwrite a test that doesn't pass, then write the code to make it passβ (though there's a bit more to it). I do TDD when it makes sense, but not enough to really call it TDD; as an everywhere phenomenon, I don't really like it.
It's not the same as writing tests, which I do do.
Also, when I was working on pet projects close to my hard, I found it really hard to keep them managable when they grew bigger.
I thought maybe doing more testing helps there, either actively to keep code functional; or passively, because writing testable code produces cleaner code, they say.
@floppy Also it really depends on what langauge you're writing in, if you're writing in a dynamic langauge you'll need a lot of extra unit tests to make sure your stuff doesn't break.
@floppy Testing is easier when you have small functions that don't mutate state (other than their arguments). I also find that's a good way to design your program; I think that's what people mean by βwriting testable code produces cleaner codeβ.
Sounds like there is some 'disbelief' in you. ^^
Dunno how to experience the value of testing when exploring it alone. I had a really great teacher and team that showed me why this is a good thing + how to do it.
Most influental in the long run was the realization that changes in a sufficiently large system become absolute nightmares without good test coverage. Which in turn means that people will go pretty far to avoid these nightmares. Which causes afditional side effects. And so on
@floppy All three are subtly different things, I think.
A pure function has no side-effects, and also returns the same thing each time. (Example: lambda x, y: x + y + 4)
A function without side-effects can return something different each time, but mustn't modify program state. (Example: lambda: time.time() )
The thing I described *can* modify state accessible from arguments, so long as it doesn't use state independent of its arguments. (Example: lambda l: l.reverse() .)
Nice examples. π
Regarding time - this can be a nightmare to test, deoending on the code. I made good experience with moving the time returning code in an own component and mock it in all test, so the result is constant/predictibale.
@wizzwizz4 True. I think it's also sometimes called pure functions or not having side effects. I think I heard that first when reading a bit from "Uncle Bob" and I'm thinking back to some bits like that frequently.
@ghost_letters It's less disbelief related to testing, but maybe more that testing requires some effort (for me, at the moment) that seems to pay off more on the long run. (And delayed gratification in front of the computer is a difficult thing to do in modern days.) π
I like to think of proper testing as a good habit that needs to be cultivated, like good coding style or proper documentation. And getting that going I found a bit difficult so far.
@floppy @ghost_letters From my personal experience: I've been writing a personal CLI app in Rust that consists of 3000 SLOC now. I've refactored it a gazillion times over the course of a few months and whenever I did I broke the unit tests I had in place because I forgot to change things in my code that the refactors had affected.
It depends on the project, I suppose, but testing gives me more peace of mind that my code does what it should. Also a built-in testing framework helps...
@floppy @ghost_letters Also I realized the value of writing modular and composable code so whenever I shift something around the whole application doesn't break down like a house of cards.
I suppose this ties into your musings of writing functions without side effects.
@floppy @ghost_letters I mean to say: in my case, tests are essential because without them I would have to spend a LOT of time debugging stuff so I actually save time with it.
And it's still just a small-ish solo project.
@floppy @theDoctor @ghost_letters Code composed of small modules is easier to test, if each module can be tested in isolation. It's then possible to drive unusual & error paths as well as the happy paths through the code.
The trick is deciding the size of "unit" to test. If it's larger, there's more scope for refactoring without needing to change the tests. If it's smaller, then moving code between modules and changing interfaces tends to require test rework.
@theDoctor Those are great points, thanks for sharing!
How would you say does continuous testing and writing modular/composable code work together? I suppose both mutually benefit each other, but I'm not sure how big that effect actually is in the end.
Ya, you have to push through this discomfort (which means learning how to use the test framework efficiently) and soon it becomes a huge and fast gratification to see all the green checkmarks.
Regarding instant gratefication - nothing came close for me to writing end to end tests with Cypress. Seeing how the web app is running in front of me and all parts behave as expect is the true nerd porn. ;)
@ghost_letters That confirms a little what I suspect too. Maybe I should challenge myself next time not with an intriguing project idea, but with something simple and the challenge is to write proper tests for it. Learning one thing at the time.
Whoa Cypress looks really amazing! I'm currently not enough in UI-design and JavaScript (that seems to be where it shines), but I'll definitely keep that in mind, thanks!
@floppy Don't be caught by the trap of thinking TDD will necessarily give you a great design. It's a good way of avoiding bad/untestable code, but it doesn't guarantee clean, understandable interfaces etc. As Rich Hickey described, using TDD to get a good design is like driving a car along the motorway and bashing against the crash barriers as a way of keeping on track.
@floppy If you want a book on TDD, this is good: http://www.growing-object-oriented-software.com/.
If you just want to learn it by doing:
1. Write a failing test.
2. Make the test pass
3. Refactor to make the code clean
4. Repeat
(A failing test might not even compile if it's calling something which hasn't been written yet. That's ok.)
I have also used coverage tools when not doing strict TDD to make most code is tested.
Hope that helps!
@underlap Those are good suggestions! The learning-by-doing way matches what I know of TDD so far. Maybe in my head I'm making it bigger than it is. Thanks also for the book recommendation, I'll have a closer look!
Chirp! is a social network. It runs on GNU social, version 2.0.1-beta0, available under the GNU Affero General Public License.
All Chirp! content and data are available under the Creative Commons Attribution 3.0 license.