5 ms·
Why is testing harder in a statically typed language? Seems about the same to me.
by chadaustin 11y ago
Why is testing harder in a statically typed language? Seems about the same to me.
- phamilton 11y agoMonkey patching is often difficult in a statically types language, but is quite useful in testing.
- chadaustin 11y agoAh, fair enough! Though monkey patching is only possible in some dynamic languages, namely the ones where classes and modules are mutable hash tables. Note that in ES6, unless you insert some special code via something like babel-rewire, imports can't be monkey patched. Also, Scheme is dynamically-typed and has no monkey patching. Anyway, I agree monkey patching in tests can be pretty convenient! But I personally think it's also a bad idea because it means that your tests are now coupled to the internal implementation of the code under test. That is, if you refactored the implementation, the test could break. Passing testable dependencies explicitly avoids this problem and avoids the need to monkey patch in tests.
- phamilton 11y agoThat seems like more of an argument against mocking in general. Even if you DI you are still expecting X method to be called on the mock object and have created that coupling. I think DHH describes a great example that illustrates a big difference in philosophy around this: http://david.heinemeierhansson.com/2012/dependency-injection-is-not-a-virtue.html http://david.heinemeierhansson.com/2012/dependency-injection...
- nrinaudo 11y agoWhile I see your point, if you ever find yourself wishing for monkey patching while writing a test in a statically typed language, it's a code smell. You've not abstracted things enough. If you need to modify things that way but can't, you should consider abstracting over it and making it possible at runtime. That way, not only do you get to write a better test - a whole class of errors is gone, just not possible anymore - you also have a more modular system, the components of which you can test individually.
- phamilton 11y agoDHH captures this very well http://david.heinemeierhansson.com/2012/dependency-injection-is-not-a-virtue.html http://david.heinemeierhansson.com/2012/dependency-injection... I think his example of system time is a good one because an abstraction in that case often only serves to enable testing. If testing is already possible, then that abstraction is just unneeded complexity
- nrinaudo 11y agoWithout any sarcasm of any kind, I expect I must be missing the point of what you linked and would love for you to clarify it. What I understood was, changing the way time is handled for your entire Ruby process in order to test that some publish function sets the right date is good practice. As opposed to, say, have it expect a date argument, which lets you test it without side effects that can impact absolutely everything else, and gets you a more powerful feature, publishAt rather than just publishNow. If I understood this correctly (which I honestly doubt), I don't think our conversation serves much purpose. You seem to regard as good practice what I consider utter lunacy. I don't pretend that I'm right, just that there's absolutely no chance that we'll agree on this. That being said, your point about introducing abstraction for the sole purpose of enabling tests is well made. My experience is that you'll often end up glad you abstracted over whatever it was you needed for the tests and use it in live code, but not 100% of the time. But if the alternative is global mutable states, I'll consider un-needed abstraction the lesser of two evils and gladly embrace it.
- phamilton 11y agoI'm surprised at the shock and awe. It's a test. It's not production code. It runs and is able to verify behavior. It runs isolated, and no other code is running when it does. Building publishAt is more powerful than publishNow, but building something more powerful than what you need is hardly good practice. And you still end up with the problem of how you test the code calling publishAt. This isn't some whack job in the corner saying something he came up with is a good idea. This is how the Ruby community thinks and writes software, with great success. And it does in fact clash with other communities. The point of this discussion isn't really whether one approach is better than the other. The point is that our tools (specifically dynamic languages with flexible metaprogramming) drastically influence our view of what is acceptable. Your strong disagreement demonstrates the point.
- nickbauman 11y agoOne of the biggest benefits of testing comes from writing the test prior to the implementation (TDD). In a statically typed language, your test won't compile until the implementation is there, meaning this process is less accessible to you. Type systems focus on making sure you write the code correctly. They help not at all whether you're writing the correct code. In the end, this is on you, the developer, to express correctness in the type system. So the theory goes if you can define the problem correctly in, say, Haskel's type system, (up front, mind you), you may not even need any tests. But most systems are built with incomplete information. You can't know everything ahead of time. Often what is ultimately "correct" is to ship sooner rather than later, learning what is systemically correct as you go along. This is where dynamic languages do well.
- dllthomas 11y ago> But most systems are built with incomplete information. You can't know everything ahead of time. Often what is ultimately "correct" is to ship sooner rather than later, learning what is systemically correct as you go along. This is where dynamic languages do well. Again, my experience is that this is precisely where the type checker gives me the most help.
- chadaustin 11y agoWell, I've done lots of TDD in both Python/PHP/JS _and_ Haskell and C++, and I don't agree at all. I was the advocate for TDD at IMVU and built many of the test frameworks and core systems across all languages. You can definitely TDD Haskell, for example, with liberal use of "undefined" or "error". Same with C++: just throw an exception in code paths you haven't "explored" with tests yet. Also, the notion that because Haskell has a strong type system you don't need tests is a persistent myth. It's nice to have types to prove certain aspects of your program, but you still need a test suite in any significant project.
- dllthomas 11y ago> Also, the notion that because Haskell has a strong type system you don't need tests is a persistent myth. It's nice to have types to prove certain aspects of your program, but you still need a test suite in any significant project. I think that given a strong type system there exist tests you might otherwise write that you don't need. And I think that is sometimes confused for "given a strong type system, all tests are unnecessary" (which, in my opinion/experience, certainly does not hold), both by some people advocating type systems and by some people reacting to advocacy of type systems.