-
-
Save meza/9069507 to your computer and use it in GitHub Desktop.
/** | |
* Disclamer: This is sort of a rant, but without any negative feelings towards the creators of the project | |
* in question. | |
* I value their desire to help people write more readable code, and wish there were more people doing this! | |
* The only thing I'm questioning is the ever growing trend of tools over craft. | |
* | |
* This is a mini blog-post in response to https://twitter.com/hhariri/status/435457449232171008 | |
* | |
* The reason I have some negative feelings is that writing readable code is not rocket | |
* science, and with tools like Spek, we're basically allowing people to be ignorant about crafting clean | |
* code. | |
* | |
* I'm not particularly happy with the pretense of the project, as the code used is indeed unreadable. | |
* I however don't see the solution in masking that unreadability by yet another tool, but in refactoring | |
* the code to make it self-documenting. | |
* | |
* With Spek, we create instant duplication of the code, in the comments. What happens if you change the | |
* income value or the expected outcome? You don't only have to change them in the test code, but the comments | |
* too. | |
* | |
* Let me quote the project readme: | |
* | |
* "Tests are specifications | |
* Tests are executable specifications of your system. They are real-time compilable code that tell you how | |
* a piece of code should behave and under what conditions is a certain outcome expected. That is why it is | |
* important to be explicit, concise and unambiguous when it comes to defining specifications." | |
* | |
* My point exactly! | |
* | |
* So without further ado, here's my version of the test in question. | |
* To be fair to the project, I haven't modified the interface of the TaxRateCalculator, although I would | |
* happily rename calculateRate() to rateOfIncome(). | |
* | |
* @author meza www.meza.hu | |
*/ | |
public class CalculatorTest { | |
@Test | |
public void testCalculateTaxRate() { | |
TaxRateCalculator calculator = new TaxRateCalculator(TaxRateCalculator.DEFAULT_LOCALE); | |
int income = 200; | |
int averageChangePerSemester = 10; | |
int actualRate = calculator.calculateRate(income, averageChangePerSemester); | |
int expectedRate = 300; | |
String messageOnFailure = String.format( | |
"The rate for an income of %d was not calculated correctly for an average change of %d per " + | |
"semester", income, averageChangePerSemester); | |
assertEquals(messageOnFailure, expectedRate, actualRate); | |
} | |
} |
And in terms of having to change the code when the specifications change, absolutely, that's the entire point.
My point is that readable test code is everything you've described just here without the need for additional comments. :)
I agree that this rarely happens, and we all need to practice the art of readability. I believe that the purposes you've described perfectly can be achieved with native code only with good variable naming, meaningful error messages and descriptive method calls. Especially if you follow your business domain in these practices, you'll end up with non-technical people being able to read code.
I might have not been clear that I'm too talking about test code, although I strongly believe that there should be no quality difference between test and prod code.
When you write a "business" application, where do you get the domain knowledge from? How do you transmit that domain knowledge to those that are new to the business and need to work on the code? Or when you come back to the code 6 months later? Or when the domain changes?
How do you know that the domain knowledge is correctly represented in the code? How do you know that the context required for a specific Tax Calculator (i.e. your Arrage code) is not missing anything? There is nothing to contrast it with. The only thing you have to contrasts the correctness of your code is some specification. If that is written down, then it's some document somewhere. If it's not written down, then...well....
Also, I think you're probably well aware that the reality in the field is far from what we'd all hope for. This is in no way meant to replace clean code, far from it. Specially from someone like me that's been anal about this :)
Yes, I am aware and really sad about it! :)
I agree with the need for specification. All I'm saying is that that specification could very well be the test code itself.
Trusting the code is imperative, and I think it's the same with any form of specification. How do you know that the specification is updated after a change? I trust tests more, as if the intent of the business domain changes, they should break. If they break, they should be updated.
Too bad that in the field, people usually test implementation more, than intent :)
Take your example:
You give the test the name:
testCalculateTaxRate
Firstly, I believe test is somewhat redundant and there for historical reasons if not anything else. You now pass in a locale to your constructor. Why? How do I know that Tax Calculations is subject to locale? This is something that someone at some point told me when speaking to the domain expert? Or is it written down somewhere? If it is, where?
Now, if you write:
given("Tax Calculator that is subject to locale changes")
this gives me something to contrast with.
Alternatively, you could write:
testCalculateTaxRateGivenATaxCalculatorThatIsSubjecToLocaleChanges
Same thing. Just one is more readable.
Of course, we rely on people having to update the specs. But tell me, when are there more chances of having that done?When the spec is staring you right in the face or it's in some some document hidden somewhere else.
There's a certain level of professionalism required, much like that of not breaking tests or ignoring failed tests.
In terms of trusting the code, I don't think it's a question of trust as it is a question of understanding.
I'm interested in this idea but I'm not sure I understand the notion of where the domain knowledge should "go", and how domain knowledge can be effectively communicated to developers new to a codebase. Would this level of domain specification not be expressed in the tests? If the developer is following a TDD cycle then the refactoring steps should have introduced appropriate abstraction in both the tests and the implementation? OCP, SRP, and ensuring there is no duplication between the tests and the implementation code should get us to this result. We should be left with test code that fully specifies the implementation, or am I missing something?
Regarding the discoverability of specifications where they are kept apart from the implementation, your codebase should be fully navigable in most modern IDEs.
OK. I fully agree with the code being readable and it seems I wasn't clear on the site (excitement of releasing I guess and doing examples at 3 am).
But the point of Spek isn't to write comments alongside code or make up for lack of readabiltiy of the code. It is to put specifications (written SPecifications that users understand also) alongside the code. What is the purpose of this?
These speks are NOT in the production code. They are part of the "test" code.