4 ms·
> TDD is a good tool for enforcing that, as you only get to write code that you have a failing test case for. TDD encourages the use of mocks and unit testing
by vrnvu 4y ago
> TDD is a good tool for enforcing that, as you only get to write code that you have a failing test case for.
TDD encourages the use of mocks and unit testing to increase code coverage. And unit testing is specially dangerous. You write a test, then program, so the test is helping you (the programmer). Selling the idea that the higher the test code coverage is the better and safer your code is. Not true at all. If your code doesn't have integration tests for example, you will never know how it actually runs. If you mock everything, you are not really "testing" anything but your internal logic. Unit testing and code coverage just checks that a code path has been run. But there are other tools like fuzzy testing or mutation testing... Do you randomize the memory at every test run? Do you make sure that the CPU cache is cold or hot depending on the test? Good testing is hard.
Most unit tests are written to ease the development. After finishing the development, they are safe to delete. Because they don't add any real value as I understand it. I understand that a test is a business contract of something that MUST work in a certain way. Unless the contract changes, the test must never be removed or changed. TDD and exhaustive unit testing make the maintenance process harder because you don't know if a test is useful or not.
If you follow TDD, most unit tests are re-written all the time. Because they were not written to test a business or critical contract, they were originally written to help some programmer write some internal logic.
- pinto_graveyard 4y ago> If you mock everything, you are not really "testing" anything but your internal logic. That's the purpose of unit tests. They do not exclude the need to perform other kinds of test. Integration tests, contract tests, stress tests - all those will focus on different facets of a system. > Most unit tests are written to ease the development. After finishing the development, they are safe to delete. This is especially bad advice, unless no one will never touch that codebase ever again. I saw old unit tests highlight bugs that would have been introduced by new code many times over the years. > TDD and exhaustive unit testing make the maintenance process harder because you don't know if a test is useful or not. Then, as a developer, remove unit tests that became useless. Code coverage is a measurement. If you turn it into a goal, it will become useless. If you have "useless" unit tests, it tells me that some unit tests were written as padding to move code coverage up.
- mpweiher 4y ago> TDD encourages the use of mocks and unit testing to increase code coverage. No, it encourages reasonable decoupling, i.e. good design. If you see yourself introducing mocks (I think you mean stubs, mocks are something more specific) everywhere, you are feeling the pressure, but avoiding the good design. https://blog.metaobject.com/2014/05/why-i-don-mock.html https://blog.metaobject.com/2014/05/why-i-don-mock.html > Most unit tests are written to ease the development. Yes, unit tests help significantly in development. > After finishing the development, they are safe to delete. Noooooooooooooooooooooooooooooooooooooooooooooooo!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! They are your guardrails against regressions. > If you follow TDD, most unit tests are re-written all the time. Nope.
- mpweiher 4y ago> guardrails against regressions. And of course that's important because it enables you to be courageous and refactor mercilessly. Which again is important because it enables you to Do the Simplest Thing That Could Possibly Work, and stick to YAGNI, because you know you can change your mind later.
- randomdata 4y ago> TDD and exhaustive unit testing make the maintenance process harder because you don't know if a test is useful or not. TDD was later given the name BDD (Behaviour Driven Development) to emphasize that you are not testing, but actually documenting behaviour. What kind of behaviour are you documenting that isn't useful and why did you find it necessary to document in the first place? > If you follow TDD, most unit tests are re-written all the time. What for? If changing requirements see that your behaviour has changed to the extent that that your unit does something completely different, it's something brand new and should be treated as such. Barring exceptional circumstances, public interfaces should be considered stable for their entire lifetime and, at most, deprecated if they no longer serve a purpose. The implementation beneath the interface may change over time, but TDD is explicit that you should not test implementation – it is not about testing – only that you should document the expected behaviour of any implementation that may carry out your desired behaviour.
- P_I_Staker 4y ago> Selling the idea that the higher the test code coverage is the better and safer your code is. I don't think someone that believes this has a good understanding of unit testing. You can easily get 100% coverage without testing anything at all! Coverage is a great metric if it's predicated on high quality tests. Even then 100% coverage doesn't equal "safe". It means that a lot of effort has been put into understanding and testing internal behavior. You still need higher order tests, arguably even more.
- rhdunn 4y agoI generally don't mock in my TDD-style approach to writing tests. If you have a dependent class, e.g. writing nested serialization logic for JSON objects, you shouldn't mock the inner classes or the JSON serialization classes. Well written unit tests serve to provide regression testing to prevent bugs reoccurring and to keep existing functionality (e.g. support for reading existing data) working. TDD is used as a way to help write tests for the API surface and usage of your classes, functions, etc.. You should generally avoid testing internal state as that can change. For example, if you are writing a set class, the logical place to start is with an empty set -- that's because it is easy to define the empty logic, defining accessor functions/properties like isEmpty, size, and contains. The next logical step is adding elements (two tests: add a single element, add multiple elements). Etc. Later on, you can change the internal logic of the set from e.g. an array to a hash map. You will keep your existing tests as they document and test your API contract and external semantics. Likewise, if your hash set uses another class like an array, or a custom structure like a red-black tree, you shouldn't mock that class.