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

时间:2016-10-15 16:56:07

标签: scala rest testing spray akka-persistence

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.

2nd update: I have tried to synchronize methods thus:

  def add( apuesta: Apuesta ): Apuesta =  synchronized {
    {
      this.apuestas += ( apuesta.quien -> apuesta )
    }
    apuesta
  }

Still no dice. I still have either to print the whole Apuesta object or else to wait in the thread, which causes more problems. What does println do that can't be done via synchronization?

Update 3: After testing for a while, println only "synchronizes" part of the time. Still fails randomly. It's got to do with timing somehow, but can't see how.

1 个答案:

答案 0 :(得分:2)

它失败了,因为你在测试之间存在依赖关系。在发出Put( "/0/2/Alguien")请求之前,必须先完成Get("/Alguien")。否则,alguien中没有info.CC_MII.Apuestas#apuestas项的条目,当您尝试访问它时,您的应用程序将失败。

您以print或直接使用Thread.wait(100)的形式介绍了延迟,并以这种方式为Put请求提供了完成时间。如果您以某种方式延迟GET的测试执行而不是将打印或睡眠放入服务中,那会好一点。

另一个选项(更好)可以将状态注入info.CC_MII.Apuestas apuestas地图的初始值。然后,您可以在设置测试期间提供一些条目,并尝试使用Get请求Alguien来获取它们。