<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    emu in blogjava

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      171 隨筆 :: 103 文章 :: 1052 評論 :: 2 Trackbacks
    自作聰明的junit.swingui.TestRunner

    問題
    在junit.swingui.TestRunner的時候發現TestRunner啟動過程中報錯:
    log4j:ERROR A "org.apache.log4j.ConsoleAppender" object is not assignable to a "org.apache.log4j.Appender" variable.
    同時也發現一個平時工作正常的類在使用junit.swingui.TestRunner進行測試的時候報告一個奇怪的 ClassCastException,明明構造的對象的類是實現了指定的接口的,可是就是無法造型到接口上。
    進一步研究發現,即使造型回原來的類也不行,雖然調試的時候顯示構造的對象就是指定的類,但是就是無法造型成這個類,一度認為是妖人作祟或者機子被落了降頭。

    研究
    求得莊老大再次出手,一下指出指出問題在于不同的類裝載器裝載的類無法相互造型的。于是進去junit.swingui.TestRunner里面去找類裝載器,一翻折騰之后終于找到:

    junit.runner.BaseTestRunner
               |------junit.swingui.TestRunner
               |------junit.textui.TestRunner

    在BaseTestRunner里面定義了這樣一個方法:
        public TestSuiteLoader getLoader() {
            if (useReloadingTestSuiteLoader())
                return new ReloadingTestSuiteLoader();
            return new StandardTestSuiteLoader();
        }
    不過注意到junit.textui.TestRunner是不會出上面的錯誤的,因為它自己重載了getLoader()方法,
        /**
         * Always use the StandardTestSuiteLoader. Overridden from
         * BaseTestRunner.
         */
        public TestSuiteLoader getLoader() {
            return new StandardTestSuiteLoader();
        }
    但是junit.swingui.TestRunner就很自作聰明了,為了避免每次在點“run”按鈕的時候裝載運行器本身,就直接使用了基類的方法去獲取裝載器),這樣基類就可以調用自己的getLoader方法來決定要啟用那個classloader:

        public TestSuiteLoader getLoader() {
            if (useReloadingTestSuiteLoader())
                return new ReloadingTestSuiteLoader();
            return new StandardTestSuiteLoader();
        }

    如果我們用sun的jdk的話,這個方法會返回一個TestCaseClassLoader對象,而這個對象在裝載class的時候總是調用creatLoader方法:
        protected TestCaseClassLoader createLoader() {
            return new TestCaseClassLoader();
        }

    返回的其實是TestCaseClassLoader。這樣如果被測試類使用了log4j的話,會造成org.apache.log4j.Appender類被 sun.misc.Launcher$AppClassLoader(也就是sun.misc.Launcher類的嵌入類AppClassLoader)裝載一次(在啟動test的過程中vm自動裝載被引用到的類),然后在運行的時候又被junit.runner.TestCaseClassLoader再裝載一次。由兩個裝載器裝載進來的類不管是不是來自同一個.class文件,都會被認為是兩個不同的類。因此就造成了上面的錯誤。
    同樣的,如果你在自己的代碼里面這樣裝載類:
    MyClass myClass = (MyClass)Thread.currentThread().getContextClassLoader().loadClass(mClassName);
    也會造成相同的問題并拋出ClassCastException。因為MyClass是在運行測試的過程由junit.runner.TestCaseClassLoader裝載的,而Thread.currentThread().getContextClassLoader()卻指向的是sun.misc.Launcher$AppClassLoader。

    解決方法
    1 java -Dlog4j.ignoreTCL junit.swingui.TestRunner
    我猜TCL是ThreadClassLoader的縮寫,這個參數的意思大概就是讓log4j忽略Thread自己的類裝載器(sun.misc.Launcher$AppClassLoader),改而使用當前Class的裝載器(junit.runner.TestCaseClassLoader)來裝載。但是這個方法只能解決log4j的錯誤報告(改變了org.apache.log4j.ConsoleAppender的裝載方式),但是對我們自己寫的代碼中的問題卻沒有作用。

    2 在我們自己的類里面寫上一段靜態代碼:
      static{
            Thread.currentThread().setContextClassLoader(MyClassFactory.class.getClassLoader());
      }
    和方法一類似,這也是在工廠類中用加載了當前lass的裝載器(TestCaseClassLoader)來代替Thread的初始化裝載器sun.misc.Launcher$AppClassLoader。這個方法可以解決我們自己代碼中的問題,并且不會帶來影響原來的其他代碼。結合第一種方法可以解決上面的兩個問題。但是如果你有好幾個工廠類,或者你用的其他包里面用了這樣的裝載方式……那你還可以試試下面的偏門:

    3 注意到BaseTestRunner要進行一個useReloadingTestSuiteLoader()判斷才決定返回哪個裝載器
    public TestSuiteLoader getLoader() {
        if (useReloadingTestSuiteLoader())
            return new ReloadingTestSuiteLoader();
        return new StandardTestSuiteLoader();
    }
    我們來看看這個判斷過程:
    protected boolean useReloadingTestSuiteLoader() {
        return getPreference("loading").equals("true") && !inVAJava() && fLoading;
    }
    嗯,里面有個inVAJava()是什么玩意兒?
    public static boolean inVAJava() {
        try {
            Class.forName("com.ibm.uvm.tools.DebugSupport");
        }
        catch (Exception e) {
            return false;
        }
        return true;
    }
    原來它是想判斷如果當前使用的是ibm的虛擬機就使用默認裝載器,但是判斷的條件也忒簡單了點,很容易就吧它給蒙過去了:
    在當前工程下創建com.ibm.uvm.tools包,在其中創建DebugSupport類:
    package com.ibm.uvm.tools;
    public class DebugSupport{}
    沒有錯,就這個空白的類,這樣就可以把junit.swingui.TestRunner給蒙倒。這樣做據說的副作用是,每次點run按鈕的時候,都要重起gui環境,但是我沒有發現有什么區別。不過要是沒有區別,人家又干嗎費那么多事呢?不解。

    參考資料

    http://mail-archives.apache.org/mod_mbox/logging-log4j-user/200301.mbox/%3C3E1F1A31.2000605@attbi.com%3E


    [點擊此處收藏本文]
    發表于 2005年04月29日 10:48 AM


    maggie 發表于2005-04-29 11:43 AM  
    果然看不不懂

    emu 發表于2005-04-29 6:02 PM  
    你將來長大了就懂了呵呵

    Pingback/Trackback 發表于2005-04-29 6:36 PM  
    試一試

    linux_china 發表于2005-05-12 8:13 PM  
    please modify excluded.properties in junit.jar,and execude some package will be all right.
    posted on 2005-05-18 14:08 emu 閱讀(2494) 評論(2)  編輯  收藏 所屬分類: java技術測試技術

    評論

    # re: 自作聰明的junit.swingui.TestRunner 2005-12-05 10:41 wuchengding
    testrunner 哪里有下載?能不能識別帶有1. Infragistics

    2. Log4net

    3. Crownwood MagicLocalLibrary

    4. Microsoft AppUpdater

    5. Microsoft Application Blocks

    6. Microsoft mshtml

    7. Microsoft Commerce Server 2002

    8. Crystal Decisions
    的控件的東西  回復  更多評論
      

    # re: 自作聰明的junit.swingui.TestRunner 2015-11-09 21:41 yqbjtu
    我也遇到了log4j錯誤問題,但是通過刪除其中一個jar包中加載的log4j就解決了,不過樓主分析的很好  回復  更多評論
      

    主站蜘蛛池模板: 美景之屋4在线未删减免费 | 中文字幕影片免费在线观看| 亚洲欧洲精品无码AV| 特色特黄a毛片高清免费观看| 国产a不卡片精品免费观看| 粉色视频成年免费人15次| 亚洲av中文无码| 男女拍拍拍免费视频网站| 亚洲综合色在线观看亚洲| 久久久久免费视频| 亚洲精品综合一二三区在线| 免费播放一区二区三区| 亚洲剧情在线观看| 日韩免费观看一级毛片看看| 日本精品久久久久久久久免费| 久久精品国产亚洲Aⅴ蜜臀色欲| 99久久国产精品免费一区二区| 亚洲国产精久久久久久久| 四虎在线成人免费网站| 亚洲熟妇无码av另类vr影视| 免费a级毛片无码a∨性按摩| 国产黄片不卡免费| 亚洲电影唐人社一区二区| 在线观看免费污视频| 一个人看的www在线免费视频| 亚洲av成人无码久久精品| 皇色在线视频免费网站| 亚洲一区二区三区高清不卡| 国产禁女女网站免费看| 你懂的网址免费国产| 久久亚洲精品专区蓝色区| 亚洲AV无码乱码在线观看性色扶| 国产成人无码免费看片软件| 亚洲综合区图片小说区| 免费一级毛片在线观看| 99爱免费观看视频在线| 日韩欧美亚洲国产精品字幕久久久| 亚洲日韩欧洲乱码AV夜夜摸 | 成人毛片18岁女人毛片免费看| 午夜在线免费视频| 日本久久久久亚洲中字幕|