Showing posts with label unit test. Show all posts
Showing posts with label unit test. Show all posts

Thursday, 3 March 2011

Getting Started with JUnit and TDD in Scala

Dear Junior

As I like the flow of test driven development, it was natural to me to try TDD when starting using Scala. As Scala runs on the JVM and interoperates well with Java it should be easy to just use JUnit. However, it was not totally obvious how to do it, and looking around the net I found no "start from scratch, this is how to do it"-tutorial. Anyway, this is how I did it, starting from scratch.


A First Test - without JUnit

Let's start with writing a test for the now-classical Money-example. Using my IDE at hand (IntelliJ), I created a file 'MoneyTest.scala' and wrote the test checking that one swedish krona is one swedish krona.
class MoneyTest {
  def should_equal_same_amount_same_currency = {
    new Money("SEK", 1) == new Money("SEK", 1)
  }
}
Let us pick this apart. This is a class (MoneyTest) that can be used to create instances of that class. Such an instance ("a MoneyTest") is in other words "an object that can test properties of Money". One of the properties it can test is that Money "should equal same amount same currency". Testing the property is done by running the method.


We have not yet got JUnit in the loop, but this is the anatomy of a unit test in place.


Get it to Compile


To get the test through the compiler we need a Money class as well, which will reside in Money.scala.
class Money(currency : String, amount : Int) {}
Yepp, that's it - class declaration and constructor rolled together.

Now we are in shape having our first unit test and enough implementation to satisfy the compiler. Time to run the test. If we want to we can run it on the interactive read-eval-print-loop (REPL).
scala> new MoneyTest().should_equal_same_amount_same_currency  
res0: Boolean = false
Well, this is actually a failing unit test, giving us the permission to hack away. 


Taking the Red Test to JUnit

However, I have come to be used to JUnit - I like the assert methods, I like how it integrates with my IDE, I like how it integrates with build servers. So, I would like to pimp up this pretty naked unit test to make use of JUnit.

First step was to add JUnit to the Scala project. In IntelliJ I created a new "External Library" named "junit" to which I added junit-4.4.jar - I happened to find it in ~/.m2/repository/junit/junit/4.4/junit-4.4.jar. Then I added the new external library to the project.


Next step is the code. As JUnit is now on the classpath, I can do the imports, annotations, and use the methods I am used to (albeit the import will be Scala-scented). 
import org.junit.Test
import org.junit.Assert._
class MoneyTest {
  @Test
  def should_equal_same_amount_same_currency = {
    assertTrue(new Money("SEK", 1) == new Money("SEK", 1))
  }
}
Looks familiar, doesn't it? Now I can run it from within IntelliJ like any JUnit-test. Running it gives another familiar message:
java.lang.AssertionError: 
at org.junit.Assert.fail(Assert.java:74)
at org.junit.Assert.assertTrue(Assert.java:37)
at org.junit.Assert.assertTrue(Assert.java:46)
at MoneyTest.should_equal_same_amount_same_currency(MoneyTest.scala:10)
...
Well, using assertTrue never gave helpful error messages. Let's do better.


Using JUnit


Let us change the assert and use assertEquals instead. This works fine as the mother-class in Scala (named AnyRef) is nothing but "java.lang.Object" so there will be an equals method around.


To be precise, there is actually a class above "AnyRef" named "Any" which include the immutable value-types in Scala that parallel the Java primitives. But that distinction does not matter here.


Changing the code to use assertEquals we get:
  @Test
  def should_equal_same_amount_same_currency = {
    assertEquals(new Money("SEK", 1), new Money("SEK", 1))
  }
 This lands me with the error message:
java.lang.AssertionError: expected:<Money@348bdcd2> but was:<Money@4a4e79f1>
at ...
at MoneyTest.should_equal_same_amount_same_currency(MoneyTest.scala:10)
OK, two Money instances with different hashcodes are obviously not considered equal, even though their data are the same. This might be what we want for entities or domain events, but not for value objects such as Money.

However  the error message looks kind of obscure due to the default toString. Let us add a custom toString to get readable messages in the future.
class Money(currency : String, amount : Int) {
  override def toString() : String = currency + " " + amount
}
Running the tests again gives:
java.lang.AssertionError: expected: Money<SEK 1> but was: Money<SEK 1>
at ...
This is definitely more readable. Arguably it would be more confusing had we not remembered the last error message: they are different instances. So, the equality test that Scala gives us from the Scala top class AnyRef (which is the same as java.lang.Object on the JVM) does not cut it for value objects - exactly as in Java.


Fixing Equality

What to do? Of course we could get into Money and add an override def equals returning true. Then we could add a testcase to provoke a failing equals, forcing us to elaborate our equals method etc. However, in Scala there is a simpler solution at hand - declare Money as a "case class".
case class Money(currency : String, amount : Int) {
  override def toString() : String = currency + " " + amount
}
Let us not get into the details, but apart from giving us well-behaving equals and hashcode, it actually makes sense for value objects to be case classes.


Green Test, Refactoring, and Ready to Start over again


The good part is that this change take us home to a green bar. There is not much refactoring to do, so now we have gone a full TDD cycle using Scala and JUnit. Time for next round. 


Now we have gotten started

Yours

   Dan 


ps Once getting started, you might want to try out some actors in scala.

Monday, 16 November 2009

Demeter Saves Mocking Fairies

Dear Junior
In a Twitter post Damian Guy recently expressed his frustration with some hard-to-unit-test code as “”Every time a mock returns a mock, a fairy dies”. (Twitter @damianguy 2009 Oct 19)
I immediately fell in love with the quote and how it “backwards” refers to a guideline on how to design (or not design) your code for testability. What has struck me recently is that this design guideline is actually nothing but the battle-proven Law of Demeter (“Don’t Talk to Strangers”).
Let me give a rather sketchy example of how you could get a report of the shipping weight of an order composed of order items.
OrderService.reportTotalWeight(order)
orderitemlist= order.items()
weight = 0
for(item in orderitemlist)
weight.add(item.weight);
return weight;
So, what is the problem? Well, imagine the unit test for the report functionality in OrderService. To properly unit test that functionality we need to rig a “test bench” with mocked versions of the “surrounding” objects. If we do not use mocks, we will actually also test the functionality in the classes of the surrounding objects as well, and then we are not really “unit-testing” any longer – more like “micro-integration-testing”.
So a setup would need to pass in a mocked order. But then comes the call “order.items()” upon which the mock need to pass back a list of objects. Further on, these objects will have their “weight” method called, so they need to be mocks as well …
OrderServiceTest.shouldSumOrderItemWeightsForOrderWeightReport()
// Given
order = new Mock()
item1 = new Mock().expect(weight).return(4711)
item2 = new Mock().expect(weight).return(42)
order.expect(items).return([item1,item2])
// When
orderservice.reportTotalWeight(order)
// Then
…
Messy, messy, messy - and we have a mock that returns mocks. Not good for the fairy population.
The underlying design problem is that the OrderService “reaches beyond” the order object to work on the order item objects. By doing this, it "extends its contact surface" to the rest of the application – and makes it hard to write a simple and complete unit test.
Well, the Law of Demeter is a 20 years old design rule in object orientation that says that an object should only talk to its immediate neighbours. The definition found in e g Wikipedia is that the allowed objects are:
  • The object itself
  • Method parameters
  • Any objects created/instantiated within the method
  • The object’s direct component objects
In many OO-supporting languages build upon C syntax, this can be viewed as “just one dot” – this is not precise, but gives the right intuition about the rule.
And here we see that the order items do not fit into that list from the point of view of the order service.
Redesigning to follow Law of Demeter would limit OrderService to reach to order, but not to order items. This requires a new method in Order: “weight of all items”.
OrderService.reportTotalWeight(order)
weight = order.weighOfAllItems()
return weight
This piece of functionality is obviously trivial to unit test (in case you insist), and will only require you to mock one object, the order.
What has happened is of course that the complexity have moved elsewhere – into the order and its “weight of all items” method.
Order.weightOfAllItems
orderitemlist= this.items()
weight = 0
for(item in orderitemlist)
weight.add(item.weight);
return weight
It is the same code as earlier – with “order” changed to “this” (and some obvious refactoring simplifications). Now unit testing of this only requires one level of mocks, as the code only talks to “its immediate neighbours”.
OrderTest.shouldSumOrderItemWeights ()
// Given
item1 = new Mock().expect(weight).return(4711)
item2 = new Mock().expect(weight).return(42)
// When
order.add(item1).add(item2)
// Then
And no fairies where killed in the testing of this code.
What I find beautiful is that these two ways lead towards the same goal. Law of Demeter was an object orientation theory invention to address coupling and coherence of designs. The save-the-fairies quote came from a pragmatic culture of wanting to produce high-quality code. I just love that they actually mean the same.
Well, when the Law of Demeter was coined, TDD was not around, “mock” meant something else, and fairies was not to be hip for a decade or two. So, finally I must praise Damian for formulating such a modern phrasing of a piece of ancient wisdom.
Yours
Dan
ps To be able to easily write unit tests without neither killing fairies nor "mocking yourself to death" is of course crucial for TDD to be fun.
pps Realising that redesigning your code for testability is of cause an effect of wanting to test the behaviour of the code, not the code per se.

Monday, 4 August 2008

Javadoc as Self Defence

Dear Junior

When working with a code base I often get into the situation where I find a piece of code that I do not understand the purpose of. I might see that it actually do something because it is called from other parts of the system, but I cannot see why, what purpose it does fill. Such a piece of code runs a lot of risk to fall under the axe, be thrown away, and replaced.

Is that not unfair? How do I dare just to discard someone else piece of work and replace it with my own? How would I react if someone else did that to my carefully crafted solutions?

Every time a programmer is about to modify or extend a piece of code, then that code stand in front of a higher court and have to justify its existence. If the code cannot convincingly plead its case, it will probably go into the land of beyond, i e into the version control attic. And in that trial, being some long windily method with non-descriptive name and having neither javadoc, nor unit tests will be equal to saying nothing to your defence.

But still, is that not unfair? Well, that might seems so, but obviously the original programmer has not thought it worthwhile to spend one minute to motivate its design. And in that case, what motivates that I spend ten minutes on trying to reconstruct the motivations? Is my time worth just a tenth of the other programmer's? To me this is much about courtesy from one programmer to another - paying respect to each others time.

To return to the code on trial; how do I try to save my code in future trials, when I might not be present in person? I try to equip them with a possibility to plead their case for themselves. I give each public method at least one single sentence of javadoc that in plain language explains, or at least indicates, the purpose it fulfil. And by just that I drastically increase the chances that the method will be understood and left to live.

Whenever possible, and that is not always the case working deep inside already existing code, I try to strengthen the case by adding objective proofs of the code's fitness, in the form of executable tests. Here the purpose of each test is to prove some aspect of the code, e g that in a set the removal of an element is independent of how many times it is added. Here again, the names of the test methods should indicate the purpose of the test, e g testShouldDeleteElementFromSetOnRemoveEvenAfterMultipleAdds. Please check out some of Dan North's writing on behaviour driven development to get some more ideas. But again, the test will help to plead the code's case and make it stay alive for yet some time.

So to radically increase your code's probability of survival, ensure on your next commit that every public class, method, or field is equipped with a proper plead of self defence in the form of at least one single sentence that explains its purpose. It is the least you can do for your creation.

Yours

Dan

Tuesday, 19 February 2008

Psychology of Tests, Testing and TDD

Dear Junior

There have been lots of talk about the benefits of test driven development, but there is one element that I think is interesting that I not have seen a reference to - the psychological process.

Pretend that we dive into the head of a programmer (e g me) and see what happens during testing. Let us firest have a look at "classical" post-fact tests and then at the TDD way of writing the test before the code.

If I write some functional code and consider myself finished I will without doubt feel immensely proud of myself - in other words my ego is on a high note. Then I sit down to test it, and let's see what can happen: either I find bugs and flaws, or I don't. If I do not find any bugs, I do not get any extra credits in my own books - I have been exactly as good as I thought I was (but I might feel that I have wasted some time). If I do find bugs I get a slap in the face - I have not been as good as I though I was (and have committed hubris, which is a severe sin on the list of things considered as sins in Sweden). So, even if testing will improve the general level of the result, the activity of testing (per se) will never give an upside, can at best have a zero outcome, and at worse a big minus. Depressing.

So, how do I treat activities that give me no upside, but possible downsides? Naturally, I tend to avoid them, and over time minimize them.

So, I tend to shy away from testing and writing tests; even though I am firmly convinced of their necessity. Depressing.

Driving the development through tests breaks that negative feedback loop. If I write the test before the code, naturally it will fail: I have no other expectation, and the red bar is not a failure. Later on, I write some code, which make the test go green, and I have actually finished on a high note. Great!

Well, actually I have not finished as any TDD-savvy would quickly point out. I have not refactored away duplication and other code smells yet. But doing so will satisfy another part of my ego, the code aesthetics lover that resides inside me. So, satisfying that part of my ego will end the job on a yet higher not.

OK, to be brutally honest: are there no part of TDDs "red-green-refactor" that have the same "can only go wrong" drawback as traditional test-last? Well, there actually are: the refactoring. At best it says: you did not mess up, at worse: you think you where so smart but you failed. So, how come I do not tend to neglect refactoring?

Well, I have already given the answer: I love refactoring, if nothing else for the pure love of nicely crafted code. But also, the refactorings are not only the last cleanup of one part of the job. They are most often also the stepping-stone from which I launch into the next part of functionality to write - so I get immediate feedback from the refactoring as well.

I think writing tests in general, and unit tests in particular are really important. Considering how many that agree, and how seldom it is practiced, we must look for explanations: ignorance, arrogance or malice do not suffice. To see how we experience "writing tests" has helped me to shed some light over the mystery.

Whatever, I think one of the drives for me to do TDD is that is make all parts of the development cycle to be fun.

Yours

Dan

Wednesday, 5 December 2007

Don't Test Your Code

Dear Junior

From time to time I get asked how to test a specific piece of code (EJB2:s or Action classes being nasty examples). Most of the times I spontaneously do not want to answer the question at all, because I think it lead us in the wrong direction.

I do not want to test my code; I want to test the behaviour of my code. The distinction might seem like hair-splitting, but I think it makes an important point. For example, if I have some order payment service I want to check that the payment information is well-balanced, that a payment request is sent to the bank, and a lot of other stuff. If we "test the code" we want to ensure all of these things as they are written.

However, if we focus on testing the behaviour we want to make sure that one idea ("generated book-keeping transaction is well-balanced") is correctly realised by the code. This approach enables us to focus on one aspect of the behaviour at a time and ask: How can I make it possible to test this behaviour. And the natural answer is to restructure to obtain testability.

So, phrasing the test-question "how do I test my code" leads us to build really elaborate testing frameworks to feed test data into the database, and then running our components in a production-like environment (e g Cactus tests). On the other hand, phrasing the question "how do I test the behaviour of my code" leads us to refactor until we have separated concerns to such a degree that each idea is possible to test in isolation.

So, don't test your code; test its behaviour.

Yours

Dan

ps One thing you can do if you redesign for testability, is that you can refactor your code to require less cumbersome and awkward mocking.