jjmerelo jjmerelo - 1 month ago 19
reST (reStructuredText) Question

Weird Scala test behavior with global variables: including a delay makes it succeed

First, the whole code is in https://github.com/JJ/spray-test
I'm using a global object (which I don't know if it is Scala proper behavior) to share state in a Spray app.

put
adds to a Map,
get
obtains values from a map, thus:

path( Segment ) { quien =>
get {
println( Apuestas) // also Thread.wait(100)
val esta_apuesta = Apuestas.get( quien )
complete( esta_apuesta )
}
}


(please check the whole file at https://github.com/JJ/spray-test/blob/master/src/main/scala/info/CC_MII/MyService.scala)

The essential parts of the test code is

"Crea apuestas correctamente" in {
Put( "/0/2/Alguien") ~> myRoute ~> check {
response.entity should not be equalTo(None)
responseAs[String] must contain("Alguien")
}

Put( "/3/0/Menda") ~> myRoute ~> check {
response.entity should not be equalTo(None)
responseAs[String] must contain("Menda")
}
}

"GET recupera apuesta correctamente" in {
Get("/Alguien") ~> myRoute ~> check {
response.entity should not be equalTo(None)
responseAs[String] must contain("Alguien")
}
}


The problem is in the first chunk of code. If I comment out the
println
statement it does not work and it fails. This is the error message:
[error] 'There was an internal server error.' doesn't contain 'Alguien' (MyServiceSpec.scala:49)

Which obviously indicates that the "get" part hasn't worked yet, or it is in another thread, or I really don't have an idea of what's the matter.

It has probably something to do with synchronization and somesuch and I should probably have and declare Apuestas in some other way, but I'm quite new at this and I very happily would be enlightened.

I have also included this as an issue in the repo with the
hacktoberbest
label, just in case someone is interested in advancing one PR in that area. https://github.com/JJ/spray-test/issues

Update: This seems to be related to this question: Unintended change to local variable of a Scala Actor, that is, actors shouldn't share state but it's up to the program to enforce it. The thing is, well, they do share state but only if we print. Will maybe introducing a delay in the thread have the same effect? I know it's all bad form and worst practice and all the rest, but as said above, I'd happily be enlightened with the proper way of doing it.

Answer

It's failing because you have a dependency between tests. Put( "/0/2/Alguien") has to be completed before you issue a Get("/Alguien") request. Otherwise, you have no entry with alguien key in the info.CC_MII.Apuestas#apuestas, and when you try to access it, your applications fails.

You introduced delay, in form of print or directly using Thread.wait(100), and in this way gave time to Put request to complete. It would be just a bit better, if you would delay somehow test execution for GET instead of putting the print or sleep into the service.

The other option (better) could be making possible to inject state into info.CC_MII.Apuestas, the initial value for apuestas map. Then you could provide few entries during tests set up, and try to fetch them with Get request instead Alguien.