π TDD, Where Did It All Go Wrong (Ian Cooper)
Video Statistics and Information
Channel: DevTernity Conference
Views: 450,787
Rating: undefined out of 5
Keywords: devternity, better testing, bdd, clean code, refactoring
Id: EZ05e7EMOLM
Channel Id: undefined
Length: 63min 55sec (3835 seconds)
Published: Wed Dec 20 2017
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.
This is a seriously good talk. Even if you don't like TDD there are a lot of good general advices about writing unit tests in this.
This is one of the best talks I ever listened to. Immediately changed the way I worked.
This is an eye opener. Let my notes speak for me:
- Test requirements, not low level
- Test public API. Given when then
- Test the exports from a module
- Focus on higher-level
- Test modules, not classes
- Test behaviors, not methods
- Think about your code as an API
- Test the abstraction, not the implementation
- Test are isolated and with shared fixture (to run quickly)
- Red-green-refactor (go fast to working code)
- No new tests during refactoring
- Heavy coupling is the problem with all software
- Thin public API
- Refactoring = changing internals
- Patterns in the refactoring
- If you're not really sure, write tests for implementation (delete the tests)
- Don't isolate classes in testing
- Private methods are implementation details
You should also watch Improving your Test Driven Development in 45 minutes - Jakub Nabrdalik https://youtu.be/2vEoL3Irgiw. I 100% believe that one of the places TDD went wrong is we all started writing unit tests with mocks and cemented our terrible architecture in place. That being said IDK if anyone like Kent Beck ever said we should do this. I wonder if this is something we just ended up twisting. There is a 5 series discussion between Martin Fowler, Kent Beck, and DHH about this that I feel like is entirely about this. Here is a link to them https://youtube.com/playlist?list=PL0psd9osbCd1qSZM7XKG2qZdX7nDDj8to
I want to do better testing. I usually just write class/method unit tests probably overusing mocks.
Most projects I've worked on, there are either no tests or abysmally bad tests.
Writing unit tests was the easiest way to for me to start testing.
Testing for me is a way re-exploring the code I've written to ensure that it works the way I expect. It's also made me much more inclined to think about my code more. I make large chunks of code into smaller, more sensible bits.
The biggest roadblock for me is that it has to be completely self driven. None of my coworkers are supportive of it. No one wants to discuss how testing could be better. No one wants to stand up for making sure there is time to improve testing practices.
It takes time. No one has respect for the learning curve.
I forced myself to learn unit testing when I joined a company some years ago that had a useless test suite. My team didn't help in this endeavor, but I was new and no one really cared if the work I was doing took an extra week. Today, I'm at another company. There's no way I could ever slip a week in to start working on the things we'd need to follow advice like what Ian Cooper is suggesting.
How can you overcome the the sense of hopelessness when no one else seems to care about testing?
Ian is too restrictive to suggest "to avoid the mocks." There are a lot of cases where mocks are the best approach for testing.
Imagine you are testing procedural code on C that draws something in the window. Its result will be painted in the window, and usually, you can't compare the window state with the desired image.
Checking that your code called correct drawing functions with correct parameters seems natural in this case. and you'll probably use mocks for this.
I like Fowler's article about this more than what Ian is talking about. https://martinfowler.com/articles/mocksArentStubs.html
I have a question on this someone might be able to answer.
I really like the idea of just testing the API and not trying to test individual classes/methods. The bit I struggle with is, say I have an method which is meant to get a percentage of a number. I want to verify with a few different inputs that it's returning the correct percentage, but I don't expose this class directly to the API. So I could write a test that targets a specific API call which just happens to use that percentage code (and then verify in the returned API results that my final result matches what I expect), but the API call I have to make involves a ton of other code which has to run in order to hit that percentage class. If my test breaks, I don't know if it was because of code in my percentage class that failed, or something in the huge amount of other code it has to walk through which failed. It also makes refactoring tricky - perhaps in that code which I called through the API someone realized they don't need the percentage call in there anymore - now confusingly a (seemingly unrelated) test which was trying to target that percentage check falls over. That would be very confusing.
You could say "well, the percentage class isn't what you are testing - if the behaviour is that when adding tax to something it needs to come to the asserted amount, whether or not it uses that percentage class is irrelevant - you are testing the final result". Which I think is fine, but then a test that checks for adding a percentage of tax starts to look identical to the test which is verifying that tax didn't exceed a certain amount, or handled decimals correctly, or took some sort of localization into account. Since these all hit the same code and run the same API call in the test, but I want to verify different things, I could end up with 20+ asserts all in the same test. Is this...ok?
TDD went astray when TDD projects had the same rate of delay & defects as non TDD projects. /anecdote != data
Evangelists claim it is the people or the method, not the idea, which while true, could just mean TDD is a rough road for the average mortal. Successes and failures litter the landscape.
Could it be TDD is NP? ...
Robotics have been emerging for a while. many bridesmaids but so far nary a bride. hope springs eternal!
Itβs pretty simple - TDD is an almost meaningless term, because thereβs soooo many different ways to do it. Your tests are brittle? Oh, well you did too much mocking. You have to update your tests with every single change, oh well they werenβt testing behavior correctly. What is behavior exactly? Donβt worry about that, just keep writing tests. Your frontend tests donβt seem valuable? Well, who said you should test frontend code?
If you read the original TDD book, Kent Beck applies it to a Money class. Itβs a value object that has methods for adding values of different currencies together, stuff like that. Why did we think that idea would scale to the entire system, and be applied in the same way across the stack?
Now, I still write a lot of tests, because I donβt know of a more practical way currently to prevent things from getting broken on a project thatβs under active development. But itβs pretty clear that TDD will not magically save your project. Tests are a very large cost, and should be used wisely.