4 ms·
> His solution works, but realistically only for small numbers. He never specified an input domain, and during development never tried to identify ambiguities b
by waterlink 11y ago
> His solution works, but realistically only for small numbers. He never specified an input domain, and during development never tried to identify ambiguities between the API 'promise' that the function would take int32, the expectation by most that it's fast to factor 2,147,483,647 (2^31-1), and the reality that his code would take a long time for that.
I think in one of the videos I watched, he explicitly said, that this is lame implementation and can be optimized (for example by looping until square root of the number). The interesting point, is that one wouldn't have to change tests for that. One would want to write a benchmark for that, to use instead of test. As in: normal tests are tests for logic, while benchmarks are tests for performance. Additionally, it is always true, that premature optimization is not a good idea. If you have an evidence, that current algorithm is not fast enough (knowledge of your dataset sizes + understanding on what would the reasonable speed be + benchmark to verify all of that), then you optimize, without behavior change.
> I think Martin's TDD style works because the problem he uses are "simple" in an algorithm sense, which is why he can talk about a 'Transformation Priority Premise'. Martin says "Refactorings are simple operations that change the structure of code without changing its behavior", but as I wrote before, 'substitute algorithm' is not a simple operation.
Funny thing I have found about 'SubstituteAlgorithm' refactoring, is its example here: http://refactoring.com/catalog/substituteAlgorithm.html http://refactoring.com/catalog/substituteAlgorithm.html - it is not changing algorithm, it simply eliminates 'if' statements. That is something that I usually do naturally, and don't even consider underlying algorithm to be changed... So maybe, Martin Fowler, when coining this term, meant something different?
> So if you also include coverage and after-the-fact tests, that would be far beyond what I've seen so far in TDD walkthroughs.
It is definitely on my mind. Since that is what I usually do. It will be hard to demonstrate code coverage in Crystal, though, since I will have to write a library for that (there is none at the moment); so I might as well go for showing test coverage use in a different language for a change (like Clojure or Ruby). After the fact tests are definitely important too.
> That said, I don't think many people will watch a code walkthrough, so don't expect much fame or uptake from your series.
I know about that, that is why I am not only doing screencasts, but additionally, I am generally blogging, speaking at meetups (and hopefully in the future on conferences), simply speak to people and debate with them (I am ENTP after all), pair-program with random people, and I have in mind joining as a teacher/mentor one of the organization we have here in Berlin to teach beginners.
> I think unit tests implicitly enforce an API on the code, even when there's no need for an API. It's a special purpose internal API designed for testing. Because they are internal functions, you should have the freedom to refactor them, but the unit test burden keeps you from doing so.
"special purpose internal API designed for testing" is actually a smell to my knowledge. This emerges, when one tries to unit-test something that is not decoupled enough, or when one has too big objects/functions, read: violating one or many points of SOLID. That is why there is such a phrase going around: "Other word for 'testable' is 'decoupled'".
I did see this happening a lot of times. Even I had to do such weird test-only APIs myself, when I had less experience. And yes, it was weird and painful to test. Now that I analyze my knowledge: indeed, the problem was is that the object/function under test was initially badly designed. If I were to go back to the past, I would tell myself: "You see here one method. I see here 4 objects.".
Currently, I use that as an indicator - whenever I need to know something "intimate" about function or object, so that I need special API; means, that function or object under test is, most probably, should be split; and dependency injection should be used to combine the parts. This works both in OO and FP world for me: it is just a question of combining independent objects or independent functions together.
Now that I think about it, all my objects/functions in the professional code I write tend to be public-API-ish. I never test private methods/functions. But I have to say, that my objects usually have only one or two public methods (and they are extremely small).
> You might want to read http://www.rbcs-us.com/documents/Why-Most-Unit-Testing-is-Wa.. http://www.rbcs-us.com/documents/Why-Most-Unit-Testing-is-Wa.... . There's a follow-up as well. He argues that most time should be spent on integration testing, and I agree.
Notion of Unit vs Integration goes on as long as I remember. And from one year to another, someone always coins "Unit tests are dead, long live Integration tests"; another year and someone says: "Integration tests are dead, long live Unit tests". It flips over and over again.
What I think, is that both are important. Which is more important, depends on lots of factors.
I am biased towards Unit testing, since I like being able to do some actual productive work on the airplane on a low-energy battery in my laptop, if I have to, and it will just simply work, because it is very well-tested. That is why, my code typically has 90-95% of unit tests and 5-10% for everything else.
---
Thanks for such a conversation, you have really provided a lot of interesting insights for me! That is quite rare to find someone, who would do that.
- dalke 11y ago> he explicitly said, that this is lame implementation and can be optimized (for example by looping until square root of the number) Was this the same video where he chastised programmers who mentioned the Sieve of Eratosthenes? Without actually explaining why that was the wrong solution? > while benchmarks are tests for performance Certainly benchmarks are useful tests. But so are edge conditions. Consider "average(i, j)". The simple solution is (i+j)/2, but if i+j overflows then the answer will be wrong. If max signed/unsigned int is allowed as an input value, or an overflow might happen in an intermediate value, then those should surely be part of the tests which drive the code. Without it, it's all too easy to have a silent integer overflow. Going back to the primes code, 2^31-1 might be seen as a performance test. Or if the code was written well in the first place, it might just be a simple bounds check. For that matter, if 'well-written unit tests ... provide a working specification of your functional code', then where's the documentation which defines the expected or at least preferred input range? Or the step to point out that the input spec is under-defined and needs clarification? > So maybe, Martin Fowler, when coining this term, meant something different? Funny thing that. If you look around, you'll see that most people quote his example almost verbatim. In general they like to replace user code with system code. This makes me think that most people either think it's trivially obvious, or haven't really thought about it. (I think few people really do algorithm development, so neither one would surprise me.) In "Refactoring: Improving the Design of Existing Code" p. 139, at https://books.google.com/books?id=HmrDHwgkbPsC&pg=PA139&lpg=PA139&dq=%22Substitute+Algorithm%22&source=bl&ots=y57hwpM6_X&sig=gx3IPpj74SWg4F6xjSf3VnKLxBE&hl=sv&sa=X&ved=0ahUKEwiExJrKwOTKAhWBwHIKHR4OCM44ChDoAQgeMAE#v=onepage&q=%22Substitute%20Algorithm%22&f=false https://books.google.com/books?id=HmrDHwgkbPsC&pg=PA139&lpg=... , you'll that Fowler says to apply "Substitute Algorithm" only if the newer algorithm is clearer. He also says that if it compiles and passes all of the existing tests, then your done. My complaint is that even a clearer algorithm can have different edge cases, so you have to analyze the code and perhaps add new tests before you are done. > is actually a smell to my knowledge My statement was based on reviewing Martin's FitNesse code some six years ago. I can no longer recall if my memory is accurate, and withdraw my comment. > from one year to another, someone always coins "Unit tests are dead, long live Integration tests"; another year and someone says: "Integration tests are dead, long live Unit tests". It flips over and over again. Coplien doesn't say that. While he believes that most unit tests are a waste, he encourages unit tests for "key algorithms for which there is a broad, formal, independent oracle of correctness, and for which there is ascribable business value". Sorting definitely falls into that category. He also points out that maintaining unit tests which are also covered by your functional tests is a waste of maintenance time, and says to get rid of those unit tests. I've always found it hard to work in an airplane, and don't travel anywhere often enough to need to worry about it. :) Thanks also for your compliment. Best of wishes.