last day (15 days later) » 

13:27
39
Q: How to avoid logical mistakes in code, when TDD didn't help?

Arseni MourzenkoI was recently writing a small piece of code which would indicate in a human-friendly way how old an event is. For instance, it could indicate that the event happened “Three weeks ago” or “A month ago” or “Yesterday.” The requirements were relatively clear and this was a perfect case for test dr...

By having a test case for it? That seems like how you discovered it afterwards, and meshes with TDD.
@Οurous: the problem is that I discovered it through a bug report, which, from a perspective of a developer, is a failure. I suppose there should be a way to change the process to avoid this situation in the first place. The loop I mentioned in my question would work; formal proof would work as well, but both seem overly complex to use for every piece of logic.
@ArseniMourzenko the root here is failure to produce readable code. Loops, proofs, or unit tests are only useful when they result in readable code. Don't avoid them because they are tedious. Avoid them when they aren't helping you make the code more readable. Readable code makes the bugs obvious. Use any dirty trick you can find to make the code readable. Pro tip: you are the worst judge of how readable your code is. So ask someone.
Writing unit tests is easy. Writing useful unit tests is another matter entirely. You only managed to do the first part, not the "useful" part which would require one to think about the edge cases more than the 'easy' cases. The 'easy' cases tend to require little thought.
You've just experienced why I'm not a fan of test driven development--in my experience most bugs caught in production are scenarios that nobody thought of. Test driven development and unit tests do nothing for these. (Unit tests have value in detecting bugs introduced through future edits, though.)
13:27
To me your case looks like "2 days ago" still.
Repeat after me: "There are no silver bullets, including TDD." There is no process, no set of rules, no algorithm you can robotically follow to produce perfect code. If there was, we could automate the whole process and be done with it.
Congratulations, you rediscovered the old wisdom that no tests can prove the absence of bugs. But If you are looking for techniques to create a better coverage of the possible input domain, you need to make a thorough analysis of the domain, the edge cases and the equivalence classes of that domain. All the old, well known techniques long known before the term TDD was invented.
Personally, you have a larger flaw in your algorithm here: You refer to "Today", but the date-math is done against UTC. No customer is going to be in UTC, so the "Today" designation is just going to appear to arbitrarily change, and be unrelated to my wall clock. I'd either just work off of 24-hour periods ("4 hours ago", "2 days ago"), or get the user's timezone and use that to construct what you do here.
I think what you gain from it is that, now you have lots of test cases for it, you are less likely to end up in an endless loop of fixing one bug and and introducing a new one. These kind of functions are very phrone to that otherwise.
Please at least allow me to read the exact time
13:27
This is a specification problem - not a code problem. The behavior for 26 hours ago wasn't well specified from the get-go. When you deliver such a feature ask your stakeholders for behaviours and use cases and gather the tests from those. It sounds like that step is missing. Not posting an answer since this is not a 'coding' suggestion but a human one.
I'm not trying to be snarky, but your question could seemingly be rephrased as "how do I think of things I didn't think of?". Not sure what that has to do with TDD.
I'm confused... what's the difference between "Yesterday" and "A day ago"?
The biggest problem here is one you apparently still haven't thought of. Showing times in this way is NOT human-friendly (at least to this human), it's annoying.
@jamesqf. I agree. From my perspective, knowing that something happened "a day ago" is pretty much useless. I cannot think of a case where I would be able to actually do something useful with this information.
ray
ray
@LorenPechtel Your logic to justify the implied "no TDD > TDD" approach is flawed. The idea that it's "OK" to avoid TDD just because some issues devs didn't think of made it to production is like saying that there's no point in checking both sides of the road before crossing because you could always get run over by a car you didn't see coming. What OP should learn here is not that "TDD is bad", but rather, and as others have mentioned, that it's not a silver bullet. TDD is still very useful in its context, especially when trying to check for dozens, or hundreds, of possible/past regressions.
13:27
Unit testing is about the ability to refactor code without introducing new bugs. It isn't about writing bug free code from scratch.
You know the solution -- you say loop through every second for the past ten years, checking for any assertion violation, and you then dismiss the solution as being too complex. You're a computer programmer. You're in the business of managing complexity. There are only 315 million seconds in ten years! I have a cheap machine on my desk right now that does four billion operations per second, so get on it. Write a program that tests every second for the last ten years and asserts if there's a problem, and then you'll know if there's a problem!
I write compilers for a living; the first thing I do when I'm trying out a new parser is get an intern to write a program that generates a few hundred thousand test cases at random and see what crashes. We have tremendous resources at our fingertips; don't shy away from using them! They are there for you to use.
@ray I'm not saying TDD is useless. I'm saying that TDD doesn't have anywhere near the value it's proponents say it does. Look at Shane's comment--he gets it about testing. It's about safe refactoring.
@EricLippert Sure, a million test cases is no problem. Evaluating whether those million test cases are right isn't so simple.
ray
ray
@LorenPechtel Shane's comment is right and you're also right that it's about safe refactoring. The problem is that that's not what your original comment was saying, IMHO. It came across more like a rant against TDD than anything else. I'm not going to defend whatever it is that TDD marketers say, but the implication that it's probably not worth the effort or something similar (I know you didn't directly state that it was useless) is what I was replying to.
@LorenPechtel - evaluating whether the large number of test cases "are right" is entirely simple, in both the case of the present question and the case of the parser (though it would be more accurate to speak of the code being right with respect to the test cases). In the case of the present question, the test cases "are right" if no assertions occur for any of them. In the case of the parser, the test cases "are right" if the parser does not crash when fed any of them.
@Hammerite If you're going to make up a million test cases you also need a million answers for them--and that means you have a piece of code that does what the code you're writing does. Unless you're porting the code that's unlikely.
13:27
@LorenPechtel - that is not true if all you're trying to test is whether some piece of code blows up when you present it with each test case. As is the situation in both of the examples we are concerned with. There is no "answer" to each test case, merely the desire that each one should run benignly.
@Hammerite No, the problem was it produced the wrong message in a case he didn't think about. That's going to take either a working piece of code or human intelligence to create the test cases.
@LorenPechtel - your message that I originally responded to was a response to Eric Lippert's comment about automatically generating test cases to test the stability of a parser, which he used as an analogy to the OP's idea (that they had dismissed out of hand) of testing their code against every second for the past ten years. This is unrelated to curated tests of what message should be produced by the software component under development by the OP in any particular case. That is not the kind of test that Lippert was discussing.
Let me ask "if you didn't use TDD how many bugs will you find in production?", TDD is a tool, your implementation is the solution!

last day (15 days later) »