2010-08-04 54 views
9

我正在測試使用ScalaTest在Scala中編寫的解析器。解析器處理一個文件時,它有一個單獨的物體,像以下:ScalaTest:Singleton對象重新初始化的問題

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

我寫測試用例是有點像這個

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) 
     } 
    } 
    } 

現在在每次迭代之前的副作用單身對象Resolver內部的迭代不會被清理。

有什麼辦法指定scalatest模塊重新初始化單身人士對象?

更新:使用Daniel的建議,我更新了代碼,還添加了更多細節。

更新:顯然它是解析器是做一些腥事。在隨後的調用中,它不會丟棄之前的AST。奇怪。因爲這是題外話,我將挖掘更多的,可能使用一個單獨的線程的討論,感謝所有的回答

最後更新:這個問題是比解析器以外的單獨的對象,這是在其他一些文件所以我不知何故錯過了它。我用Daniel Spiewak的答覆解決了這個問題。這是骯髒的做事方式,但它也是唯一的事情,考慮到我的情況,並且考慮到我正在寫一個測試代碼,而這個代碼並沒有投入生產使用。

+0

'R'裏面是否有清理方法,或者您希望重新創建嗎? – 2010-08-04 22:15:04

+0

R中沒有清理方法,我無法更改代碼。 你可以重新創建斯卡拉單身人士對象嗎? – thequark 2010-08-04 22:50:57

+0

沒有。他們嚴格單身。按需初始化(由JVM的類加載確定),此後是永久的。當然,它們可以具有可變內部狀態,就像任何Scala類一樣。 – 2010-08-04 23:52:06

回答

7

根據語言規範,不,沒有辦法重新創建單例對象。然而,可以反射性地調用單,這將覆蓋內部MODULE$字段,它包含的實際值單的構造:

object Test 

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

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

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

現在,我已經與您共享了邪惡的祕密,讓我謹慎從未,有史以來要做到這一點。我會非常努力地調整代碼,而不是玩這種偷偷摸摸的技巧。然而,如果事情如你所說,而且你確實沒有別的選擇,那至少是一些東西。

+1

以這種方式重新初始化單例對象,但我的問題仍然存在。我不知道如何。 基本上,如果我單獨測試每個輸入文件,它的工作正常,但如果我一起測試它們,我會得到錯誤。如果我改變順序,我也會得到一個錯誤(與以前的測試案例相關的錯誤消息!)。我懷疑這可能是原因,因爲單身物體有前面迭代的副作用。 – thequark 2010-08-04 23:41:41

+0

試過,得到這個... java.lang.NoSuchMethodException:com.whatever.SingletonObject $。 () – jm0 2015-12-21 07:46:24

+0

我不得不將上面的@ daniel-spiewak示例更改爲 'val c = Test.getClass.getDeclaredConstructor()' – 2016-08-23 09:49:26

4

ScalaTest有幾種方法可以讓您在測試之間重新初始化事物。但是,這個特定的問題很難在不知情的情況下回答。主要問題是,重新初始化單例對象需要什麼?如果單例對象不能在沒有實例化新的單例對象的情況下重新初始化,那麼您需要確保每個測試重新加載單例對象,這將需要使用自定義類加載器。不過,我很難相信有人會這樣設計。你能用更多的細節來更新你的問題嗎?稍後我會再看一看,看看額外的細節是否讓答案更加明顯。

ScalaTest有一個runpath,爲每次運行重新加載類,但不是測試路徑。所以你必須推出自己的。這裏真正的問題是,有人設計了這種方式,不容易測試。我會考慮在每次測試中加載一個URLClassLoader的Resolver和Parser。這樣你會得到一個新的Resolver每個測試。

您需要將Parser &解析器關閉類路徑並關閉運行路徑。把它們放到他們自己的目錄中。然後爲每個指向該目錄的測試創建一個URLClassLoader。然後在該類加載器上調用findClass(「Parser」)來獲取它。我假設Parser指的是Resolver,在這種情況下,JVM將返回加載Parser的類加載器來獲得Resolver,這是您的URLClassLoader。在解析器上執行newInstance以獲取實例。這應該可以解決您的問題,因爲您將爲每個測試獲得一個新的Resolver單例對象。

+0

創建新的解析器不會重新初始化單例對象,並且解析器對象的創建是在循環內。 (檢查更新後的代碼) 使代碼在一個執行軌跡中接受單個輸入文件。 – thequark 2010-08-04 23:49:56