4 ms·
TDD seems to be mentioned in a lot of responses here. I'm warming up to the idea but frankly not many folks I've come across in my (short, 3 year) career seem t
by sk1pper 9y ago
TDD seems to be mentioned in a lot of responses here. I'm warming up to the idea but frankly not many folks I've come across in my (short, 3 year) career seem to understand tests really, definitely not TDD. So I've been having to learn the hard way.
My main thing with TDD right now is - how do I avoid writing tests that are too tightly coupled? I've gotten burned in the past, not even doing TDD, with tests that "know too much" and end up being more hassle than help.
Any good resources on TDD and general testing strategy that anyone can recommend?
- hex13 9y agoYou could divide (in your head) contract and implementation details. Contract - what "unit" should do, e.g. factorial function should compute factorial for given numbers. But how exactly this is done is implementation detail (will be used recursion? do/while loop? will there be result caching?) The trick is to omit implementation details from testing, because implementation can change, but after all fact(4) should return 24 no matter what. So it's useful to think in categories of contract given unit should fulfill instead of testing everything for the sake of testing.
- zachrose 9y agoThree answers: 1) Just keep going. Experiencing the pain of less-than-ideal tests is pretty much the best way to learn. It also frees you from outdated dogma about testing. 2) Maybe try testing at different levels? I got into testing by learning to write big slow acceptance tests for a particular user experience and small fast unit tests for a particular module. This helped me see what each kind of test was best for. (Maybe your "big tests" are written in Cucumber and automate a web browser, maybe they test an HTTP API, that's up to the kind of work and environment you're in.) 3) Watch this video "Boundaries" by Gary Bernhardt: https://www.destroyallsoftware.com/talks/boundaries https://www.destroyallsoftware.com/talks/boundaries
- flukus 9y ago> I'm warming up to the idea but frankly not many folks I've come across in my (short, 3 year) career seem to understand tests really, definitely not TDD. So I've been having to learn the hard way. You aren't wrong, the state of unit tests in the wild is dreadful. Many were forced into creating tests by management decry, others just think they know what unit testing is. Sometimes the latter are even unit testing advocates. Remember what a unit is, it's not a class or the method, it's the unit of behavior you are testing. In practice this basically means whatever you are asserting. Stick to the single assert principle and keep tests small. This doesn't mean only one assert statement, but only one behavior. Often asserting that a function returns an object will one test (it didn't return null), the details of the fields that object contains (like did we format "LastName, FirstName" properly) will be different tests. If you are asserting "LastName, FirstName" in 10 tests then 10 tests will break when that behavior changes. Only one test should break. Always have a setup method that creates the owner of the unit and any of it's (mocked) dependencies. When a behavior is added it should take less than a minute to add the test for that behavior (assuming the test fixture already exists). Do that and you're writing better tests than 95%+ of the industry.
- pvdebbe 9y agoTests and TDD are different things. I view TDD as a workaround for a lack of a REPL. When the code is finished, the tests can be written or polished but writing them before any code is just one variation of testing.
- zepolen 9y agoThe point of TDD is you can define the behavior before writing the code. ie. you define the expected result: def test_sum(): assert sum(1, 2, 3) == 6 def test_sum_no_args(): assert sum() == 0 def test_sum_negatives(): assert sum(-1, -2) == -3 etc, and then you write the function. This means you only do the "REPL" testing once ever for each case - and you can rerun the tests for all future changes. If ever type the same function + args into the REPL more than once - you have done redundant work and are wasting your time. Likewise if you're in the habit of writing tests after your function is made - it shows you haven't put much thought, which means you haven't done the proper analysis of why the function exists, if the function is really one function or should be two functions, what arguments it can take, what it should return etc.
- arenaninja 9y ago> if you're in the habit of writing tests after your function is made - it shows you haven't put much thought, which means you haven't done the proper analysis of why the function exists, if the function is really one function or should be two functions, what arguments it can take, what it should return etc. I'd say that's a pretty broad brush you're painting there with
- zepolen 9y agoI'm not wrong.
- arenaninja 9y agoI would say that you are, but you've convinced yourself otherwise. But testing is important, for sure.
- pmarreck 9y agoYou know how you manually test code sometimes in a console, like a REPL? Automate that and you have a unit test. Do it right before you write the implementation (forcing you to first consider how it might work, not a terrible exercise), and you have TDD. Every unit of code should be smallish, focused, and with the absolute minimum of external dependencies. That will make it easily unit-testable and far more maintainable long-term. There's finally empirical data emerging that TDD is a labor-saving device... Look up the Nagappan paper. (There's more than that now, though, that's just the most famous one.) Also, as someone else mentioned, watch the Boundaries talk by Gary Bernhardt. Amazing.