There’s an old database test suite that I’m trying to migrate from Specs to Specs2. However, Specs2 runs the tests in a weird order (from my point of view), which breaks the tests since they change the database state, and run certain code twice.
Find below a simplified version of the tests. As far as I’ve understood, the tests should run in this order: (since I’ve specified sequential):
! 222, ! 333, ! 444
However, what actually happens, is that they’re executed in this order:
! 333, ! 222, ! 444
Here are the tests:
object IncludedSpec extends Specification {
sequential
println("sssstart")
"IncludedSpec" should {
println("..222")
"context free block" >> {
println("....! 222 the first test")
1 === 1
}
var variableN = 0
"block with context" >> {
println("....333")
"using one variable" >> {
println("......! 333 doing some tests and setting variableN")
variableN = 123
}
println("....444")
"using it again" >> {
println("......! 444 testing variableN")
variableN === 123
}
println("....555")
}
}
println("eeeend")
}
Here is all println output:
sssstart
eeeend
sssstart
eeeend
..222
....333
......! 333 doing some tests and setting variableN
....444
....555
....! 222 the first test
......! 444 testing variableN
And my two questions:
-
Why isn’t
! 222executed first? -
How is it possible that
sssstart eeeendis output twice? The spec is an object and isn’t created twice?
Oddly enough, if I remove the side effects from the tests — that is, removing variableN and replacing the test bodies with ok — the tests run in the correct order.
Version details: I’m running these tests with Paly Framework 2.1-SNAPSHOT (version 203df0e from October 28 2012) and Scala 2.10.0-RC1. I think the Specs2 version bundled with Play is version 1.12, because the inline method is available, and it was added in 1.12(-SNAPSHOT), see https://github.com/etorreborre/specs2/issues/87 and there are no later Specs2 versions.
(Oh, and if you think I should completely rewrite the tests, then have a look at this question instead: How design a Specs2 database test, with interdependent tests? )
Originally, in specs2, you could create the following:
1 – an example with
in:"this is an example" in { 1 must_== 1 }2 – an example with
>>:"this is an example" >> { 1 must_== 1 }3 – a block of examples with
>>The important thing was that
inwas reserved for examples alone and accepted anything that could be converted to aResult. On the other hand>>could be used for both examples and groups of examples (to have a uniform style of nesting) so it accepted values of typeExampleorResult.Now things started to become a bit more complicated when you wanted to do the following:
1 – use
foreachto create a group of examples2 – use
foreachto create a group of expectationsThe trouble is, those 2 expressions with
foreachboth have typeUnit. But they’re doing 2 very different things! The first one is building examples, so this expression needs to be evaluated right away to build theSpecification. The second one is creating the body of anExampleand will be executed later (or maybe never if the example is filtered out for example). Two things, with the same operator>>, that cannot work.So it was decided that
>>(block: =>Unit)means “this builds a group of examples by a side-effect”, andin(expectations: =>Unit)means “this builds the body of anExampleprobably having expectations which will be a side-effect.Now, when this explains a bit better why you’re seeing a strange order with your print statements:
are printed first because they are contained in blocks of type
Unit, interpreted as groups of Examples.And :
are printed consequently because they are in blocks of type
MatchResult[_], that is, they are considered as the body of examples.I agree that this is confusing and I hope that this explanation brings some perspective on why things are the way they are. Of course another lesson is “side-effects are sneaky because they don’t tell you what they are really doing”.
So the general specs2 tip is to always end your blocks with proper values (unless you use a
foreachconstruct as shown in my example above). For example, addingokat the end of the blocks where you do a variable assignment should solve your issue.