Could you explain why the difficulty I faced in refactoring Clojure code is not issue for you?
Could you explain why the difficulty I faced in refactoring Clojure code is not issue for you?
I've watched Rich Hickey's talks and he makes fun of TDD as a design tool. And that's it. This doesn't negate the value of automated testing in general. Testing helps to ensure that the primary use-cases of your software system are fulfilled and to guard against regressions. When you get a bug, you write a test for it. When you forget about a business rule and find out you broke it later, you write a test for it. When you refactor code, it's very useful to have a suite of tests that ensure at least the main business logic still works.
TDD on the other hand is something different. Writing the test case first, before writing the code that passes it, always seemed like a dumb idea to me. So I always write my tests after and I'm not crazy about good code coverage either. But you do need tests for non-toy projects.
TDD is not designed to be a good technique for developing algorithm's but rather for designing OO systems and discovering the collaborations between various objects within a system. This doesn't mean you can forget about solid OO design principals and just somehow land up with a good design just because you use TDD.
If you've not seen that before, it's seriously good. My top video of 2013
By the way; I don't think Rich makes fun of TDD, but of how little of TDD is left when you're working with purely functional, side-effect free, code: passing functions data and verifying that you get back the right thing.
In practice every Clojure coder will tell you debugging is still a pain but that it's not a problem as long as you check each small, pure, function for correctness ... which is pretty close to, if not the same as, TDD.
----
Edit to subdue harshness... I wrote a huge ETL tool in Python using all pure functions, list comprehensions, only named_tuple, no classes, no mutation, lots and lots of garbage, but it worked and worked well. I took a two pronged approach to testing
1. Proved each pure function in separate file, this was for me, this stuff never got run again. It was like a nursery for pure transformations
2. Wrote high level integration tests, 1 or 2 per module (about 20 total)
3. If I had a nasty bug, I would set a breakpoint in the debugger (pdb) and duplicate all of the state around me as best I could and put it into a functional test that WOULD get rerun as part of the automatic testing cycle. I would love a tool that could extract program state and put it into a test for me.
4. The majority of testing was to run the ETL tool on subsets of the input and validate the output, I had a handful of these of increasing complexity. The output validation was automatic.
Testing a 4-10 line functions is waste once it is constructed. Higher level, whole module tests should cover an individual breakage of a smaller pure function. Tests are baggage (sometimes useful), just as static types are baggage (also sometimes useful). We need baggage, gotta wear clothes, read and eat.
Amen. I've wanted this so many times in Java. A really hacky way to do this is to serialize your objects while debugging and deserialize them in your test. The problem is if the classes change your test won't work because you can't deserialize anymore. Encapsulation and data hiding really get in the way here.
I don't like core.typed because what little research I've done shows that it actually becomes more verbose than java in some cases. That really seems like a step backwards.
Anyway, you don't have to believe what he believes; for example Brian Marick's Midje. (https://github.com/marick/Midje) Whatever makes you the best Clojure programmer is right for you.
It might also be that typing just isn't that beneficial for the code I'm writing.