Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ScalaTest: Issues with Singleton Object re-initialization

I am testing a parser I have written in Scala using ScalaTest. The parser handles one file at a time and it has a singleton object like following:

class Parser{...}
object Resolver {...}

The test case I have written is somewhat like this

   describe("Syntax:") {
    val dir = new File("tests\\syntax");
    val files = dir.listFiles.filter(
                    f => """.*\.chalice$""".r.findFirstIn(f.getName).isDefined);

    for(inputFile <- files) {
      val parser = new Parser();
      val c = Resolver.getClass.getConstructor();
      c.setAccessible(true);
      c.newInstance();

      val iserror = errortest(inputFile)
      val result = invokeparser(parser,inputFile.getAbsolutePath) //local method
      it(inputFile.getName + (if (iserror)" ERR" else " NOERR") ){
      if (!iserror) result should be (ResolverSuccess()) 
        else if(result.isInstanceOf[ResolverError]) assert(true)
      }
    }
  }

Now at each iteration the side effects of previous iterations inside the singleton object Resolver are not cleaned up.

Is there any way to specify to scalatest module to re-initialize the singleton objects?

Update: Using Daniel's suggestion, I have updated the code, also added more details.

Update: Apparently it is the Parser which is doing something fishy. At subsequent calls it doesn't discard the previous AST. strange. since this is off topic, I would dig more and probably use a separate thread for the discussion, thanks all for answering

Final Update: The issue was with a singleton object other than Resolver, it was in some other file so I had somehow missed it. I was able to solve this using Daniel Spiewak's reply. It is dirty way to do things but its also the only thing, given my circumstances and also given the fact I am writing a test code, which is not going into production use.

like image 910
thequark Avatar asked Aug 04 '10 21:08

thequark


2 Answers

ScalaTest has several ways to let you reinitialize things between tests. However, this particular question is tough to answer without knowing more. The main question would be, what does it take to reinitialize the singleton object? If the singleton object can't be reinitialized without instantiating a new singleton object, then you'd need to make sure each test loaded the singleton object anew, which would require using custom class loaders. I find it hard to believe someone would design something that way, though. Can you update your question with more details like that? I'll take a look again later and see if the extra details makes the answer more obvious.

ScalaTest has a runpath that loads classes anew for each run, but not a testpath. So you'll have to roll your own. The real problem here is that someone has designed this in a way that it is not easily tested. I would look at loading Resolver and Parser with a URLClassLoader inside each test. That way you'd get a new Resolver each test.

You'll need to take Parser & Resolver off of the classpath and off of the runpath. Put them into a directory of their own. Then create a URLClassLoader for each test that points to that directory. Then call findClass("Parser") on that class loader to get it. I'm assuming Parser refers to Resolver, and in that case the JVM will go back to the class loader that loaded Parser to get Resolver, which is your URLClassLoader. Do a newInstance on the Parser to get the instance. That should solve your problem, because you'll get a new Resolver singleton object for each test.

like image 32
Bill Venners Avatar answered Dec 12 '22 18:12

Bill Venners


According to the language spec, no, there is no way to recreate singleton objects. However, it is possible to reflectively invoke the constructor of a singleton, which overwrites the internal MODULE$ field which contains the actual singleton value:

object Test

Test.hashCode    // => e.g. 779942019

val c = Test.getClass.getConstructor()
c.setAccessible(true)
c.newInstance()

Test.hashCode    // => e.g. 1806030550

Now that I've shared the evil secret with you, let me caution you never, ever to do this. I would try very very hard to adjust the code, rather than playing sneaky tricks like this one. However, if things are as you say, and you really do have no other option, this is at least something.

like image 86
Daniel Spiewak Avatar answered Dec 12 '22 18:12

Daniel Spiewak