Dealing with fixtures in Scala
Sometimes you come across something that you really want to use it but you are not very convinced if you are using it the right way; this exactly is what happened to me with fixtures.
According to ScalaTest:
"a test fixture is composed of the objects and other artifacts, like files, sockets, database connections, etc, which tests use to do their work."
At first glance, I thought: "Well, this could be the next code shortener to my projects". And I was quite right, but I had to take care of certain things, specially case classes with mutable objects.
Fixtures and mutable objects
I was trying to test some cascading methods from a case class
, kind of a fluent interface. Let's imagine we have our class pizza:
What we have here is a Pizza
which can change its ingredients
depending of the allergens
of who is going to eat it. By using the public method isAllergicToSomething()
we can subtract all the ingredients that match any allergens, thanks to our private def removeIngredients()
.
But, how do we test that, when isAllergicToSomething()
is called, the dangerous ingredients are really gone from the list of ingredients
? Are fixtures suitable for this?
As a lazy developer, I thought of fixtures and how I can use them to save some time and effort, so I started to create a fixture, according to ScalaTest:
What I did is create a Trait in which I define two variables: allergens
and an instance of a Pizza
, which is stored in another file, Generators.scala
:
Doing some tests
So I decided to write some tests. I like to use FlatSpec because it is more verbose:
I ran the test and it was OK. Nice, but what if I would like to test with other allergen? Also, this time we are going to test if Tomato remains:
Let's run both tests:
What I see here is that, for some reason, it remains the instance from the first test to the another. And the answer may be easy for you, that are reading right now this and I guided you to this error. But trust me, I spent a little bit more of time figuring out what was going on. Not an ideal situation for a lazy dev.
The problem is Generators.scala
; while I was using the trait AllergicCase
, I was storing an instance of Pizza
created on Generators.scala
and not generating it within the trait, so we were actually modifying the instance on Generators
object and not creating one for each test.
The solution
So I decided to make some changes. Let's get rid of Generators.scala
and instantiate all of it within the trait:
Now I rerun the tests, and both were OK. Sweet!
Conclusion
Fixtures are a great tool for lazy devs, it encapsulates the environment by creating within the test itself all the variables and objects you need to run the test, and it does every time you run a specific test, so it gives you freedom to start over again to test everything from one trait. While this is great, it may lead you to create one specific case valid for all of your tests. Remember (note to self) that you can create more than just one trait to create all the environments/cases you need to assure your code is bombproof.
If you want to discuss about this, feel free to tweet me.