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

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

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

    posts - 97,  comments - 93,  trackbacks - 0
    軟件開發(fā)習(xí)慣中一個細(xì)微更改都可能會對軟件質(zhì)量產(chǎn)生巨大改進。將單元測試合并到開發(fā)過程中,然后從長遠(yuǎn)角度來看它可以節(jié)省多少時間和精力。本文通過使用代碼樣本說明了單元測試的種種好處,特別是使用 Ant 和 JUnit 帶來的各種方便。

    測試是大型開發(fā)過程中的基本原則之一。在任何職業(yè)中,驗證都是一個重要部分。醫(yī)生要通過驗血來確診。波音公司在研制 777 的過程中對飛機的每個組件都進行了精心測試。為什么軟件開發(fā)就應(yīng)該例外呢?

    以前,由于在應(yīng)用程序中將 GUI 和商業(yè)邏輯緊密聯(lián)系在一起,這就限制了創(chuàng)建自動測試的能力。當(dāng)我們學(xué)會通過抽象層將商業(yè)邏輯從界面中分離出來時,各個單獨代碼模塊的自動測試就替代了通過 GUI 進行的手工測試。

    現(xiàn)在,集成開發(fā)環(huán)境 (IDE) 能在您輸入代碼的同時顯示錯誤,對于在類中快速查找方法具有智能探測功能,可以利用語法結(jié)構(gòu)生成彩色代碼,而且具有許多其它功能。因此,在編譯更改過的代碼之前,您已經(jīng)全盤考慮了將構(gòu)建的類,但您是否考慮過這樣的修改會破壞某些功能呢?

    每 個開發(fā)者都碰到過更改“臭蟲”。代碼修改過程可能會引入“臭蟲”,而如果通過用戶界面手工測試代碼的話,在編譯完成之前是不會發(fā)現(xiàn)它的。然后,您就要花費 幾天的時間追蹤由更改所引起的錯誤。最近在我做的一個項目中,當(dāng)我把后端數(shù)據(jù)庫由 Informix 更改到 Oracle 時就遇到了這種情況。大部分更改都十分順利,但由于數(shù)據(jù)庫層或使用數(shù)據(jù)庫層的系統(tǒng)缺少單元測試,從而導(dǎo)致將大量時間花費在嘗試解決更改“臭蟲”上。我花了 兩天的時間查到別人代碼中的一個數(shù)據(jù)庫語法更改。(當(dāng)然,那個人仍是我的朋友。)

    盡管測試有許多好處,但一般的程序員對測試都不太感興趣,開始時我也沒有。您聽到過多少次“它編譯了,所以它一定能用”這種言論?但“我思,故我在”這種原則并 適用于高質(zhì)量軟件。要鼓勵程序員測試他們的代碼,過程必須簡單無痛。

    本文從某人學(xué)習(xí)用 Java 語言編程時所寫的一個簡單的類開始。然后,我會告訴您我是如何為這個類編寫單元測試,以及在編寫完它以后又是如何將單元測試添加到構(gòu)建過程中的。最后,我們將看到將“臭蟲”引入代碼時發(fā)生的情況。

    從一個典型類開始

    第一個典型的 Java 程序一般都包含一個打印 "Hello World" 的 main() 。在清單 1 中,我創(chuàng)建了一個 HelloWorld 對象的實例并調(diào)用 sayHello() 方法,該方法會打印這句習(xí)慣說法。



    清單 1. 我的第一個 Java 應(yīng)用程序 "Hello world"
    /*
    * HelloWorld.java
    * My first java program
    */
    class HelloWorld {
    /**
    * Print "Hello World"
    */
    void sayHello() {
    System.out.println("Hello World");
    }
    /**
    * Test
    */
    public static void main( String[] args ) {
    HelloWorld world = new HelloWorld();
    world.sayHello();
    }
    }

    main() 方法是我的測試。哦噢!我將代碼、文檔、測試和樣本代碼包含在了一個模塊中。保佑 Java!但隨著程序越變越大,這種開發(fā)方法很快就開始顯現(xiàn)出了缺陷:

    • 混亂
      類接口越大, main() 就越大。類可能僅僅因為正常的測試而變得非常龐大。
    • 代碼膨脹
      由于加入了測試,所以產(chǎn)品代碼比所需要的要大。但我不想交付測試,而只想交付產(chǎn)品。
    • 測試不可靠
      既然 main() 是代碼的一部分, main() 就對其他開發(fā)者通過類接口無法訪問的私有成員和方法享有訪問權(quán)。出于這個原因,這種測試方法很容易出錯。
    • 很難自動測試
      要進行自動測試,我仍然必須創(chuàng)建另一程序來將參數(shù)傳遞給 main()

    類開發(fā)
    對我來說,類開發(fā)是從編寫 main() 方法開始的。我在編寫 main() 的時候就定義類和類的用法,然后實現(xiàn)接口。它的一些明顯的缺陷也開始顯現(xiàn)出來。一個缺陷是我傳遞給 main() 來執(zhí)行測試的參數(shù)個數(shù)。其次, main() 本身在進行調(diào)用子方法、設(shè)置代碼等操作時變得很混亂。有時 main() 會比類實現(xiàn)的其余部分還要大。

    更簡單的過程
    我原來的做法有一些很明顯的缺陷。因此,讓我們看看有什么別的方法可以使問題簡化。我仍然通過接口設(shè)計代碼并給出應(yīng)用示例,正如原來的 main() 一樣。不同的是我將代碼放到了另一個單獨的類中,而這個類恰好是我的“單元測試”。這種技術(shù)有以下幾點好處:

    • 設(shè)計類的一種機制
      因為是通過接口進行開發(fā),所以不太可能利用類的內(nèi)部功能。但因為我是目標(biāo)類的開發(fā)者,我有到其內(nèi)部工作的“窗口”,所以測試并不是個真正的黑箱。僅憑這一點就足夠推斷出需要開發(fā)者本人在編寫目標(biāo)類的同時負(fù)責(zé)測試的開發(fā),而不是由其他任何人代勞。
    • 類用法的示例
      通過將示例從實現(xiàn)中分離出來,開發(fā)者可以更快地提高速度,而且再不用在源代碼上糾纏不清。這種分離還有助于防止開發(fā)者利用類的內(nèi)部功能,因為這些功能將來可能已經(jīng)不存在了。
    • 沒有類混亂的 main()
      我不再受到 main() 的限制了。以前我得將多個參數(shù)傳遞給 main() 來測試不同的配置。現(xiàn)在我可以創(chuàng)建許多單獨的測試類,每一個都維護各自的設(shè)置代碼。

    接下來我們將這個單獨的單元測試對象放入構(gòu)建過程中。這樣,我們就可以提供自動確認(rèn)過程的方法。

    • 確保所做的任何更改都不會對其他人產(chǎn)生不利影響。
    • 我們在進行源碼控制之前就可以測試代碼,而無需等待匯編測試或在夜晚進行的構(gòu)建測試。這有助于盡早捕捉到“臭蟲”,從而降低產(chǎn)生高質(zhì)量代碼的成本。
    • 通過提供增量測試過程,我們提供了更好的實現(xiàn)過程。如同 IDE 幫助我們在輸入時捕捉到語法或編譯“臭蟲”一樣,增量單元測試也幫助我們在構(gòu)建時捕捉到代碼更改“臭蟲”。




    回頁首


    使用 JUnit 自動化單元測試

    要使測試自動化,您需要一個測試框架。您可以自己開發(fā)或購買,也可以使用某些開放源代碼工具,例如 JUnit。我選擇 JUnit 出于以下幾個原因:

    • 不需要編寫自己的框架。
    • 它是開放源代碼,因此不需要購買框架。
    • 開放源代碼社區(qū)中的其他開發(fā)者會使用它,因此可以找到許多示例。
    • 它可以讓我將測試代碼與產(chǎn)品代碼分開。
    • 它易于集成到我的構(gòu)建過程中。

    測試布局
    圖 1 顯示了使用樣本 TestSuite 的 JUnit TestSuite 布局。每個測試都由若干單獨的測試案例構(gòu)成。每個測試案例都是一個單獨的類,它擴展了 TestClass 類并包含了我的測試代碼,即那些曾在 main() 中出現(xiàn)的代碼。在該例中,我向 TestSuite 添加了兩個測試:一個是 SkeletonTest,我將它用作所有新類和 HelloWorld 類的起點。



    圖 1. TestSuite 布局
    TestSuite 布局

    測試類 HelloWorldTest.java
    按照約定,測試類的名稱中包含我所測試的類的名稱,但將 Test 附加到結(jié)尾。在本例中,我們的測試類是 HelloWorldTest.java 。我復(fù)制了 SkeletonTest 中的代碼,并添加了 testSayHello() 來測試 sayHello() 。請注意 HelloWorldTest 擴展了 TestCase。JUnit 框架提供了 assertassertEquals 方法,我們可以使用這些方法來進行驗證。 HelloWorldTest.java 顯示在清單 2 中。



    清單 2. HelloWorldTest.java
    package test.com.company;
    import com.company.HelloWorld;
    import junit.framework.TestCase;
    import junit.framework.AssertionFailedError;
    /**
    * JUnit 3.2 testcases for HelloWorld
    */
    public class HelloWorldTest extends TestCase {
    public HelloWorldTest(String name) {
    super(name);
    }
    public static void main(String args[]) {
    junit.textui.TestRunner.run(HelloWorldTest.class);
    }
    public void testSayHello() {
    HelloWorld world = new HelloWorld();
    assert( world!=null );
    assertEquals("Hello World", world.sayHello() );
    }
    }

    testSayHello() 看上去和 HelloWorld.java 中原來的 main 方法類似,但有一個主要的不同之處。它不是執(zhí)行 System.out.println 并顯示結(jié)果,而是添加了一個 assertEquals() 方法。如果兩個值不同, assertEquals 將打印出兩個輸入的值。您可能已經(jīng)注意到這個方法不起作用!HelloWorld 中的 sayHello() 方法不返回字符串。如果我先寫過測試,就會捕捉到這一點。我將 "Hello World" 字符串與輸出流聯(lián)結(jié)起來。這樣,按照清單 3 中顯示的那樣重寫了 HelloWorld,去掉 main() ,并更改了 sayHello() 的返回類型。



    清單 3. Hello world 測試案例。
    package com.company;
    public class HelloWorld {
    public String sayHello() {
    return "Hello World";
    }
    }

    如果我保留了 main() 并修改了聯(lián)系,代碼看上去如下:

      
    public static void main( String[] args ) {
    HelloWorld world = new HelloWorld();
    System.out.println(world.sayHello());
    }

    新的 main() 與我測試程序中的 testSayHello() 非常相似。是的,它看上去不象是一個現(xiàn)實世界中的問題(這是人為示例的問題),但它說明了問題。在單獨的應(yīng)用程序中編寫 main() 可以改進您的設(shè)計,同時幫助您設(shè)計測試。現(xiàn)在我們已經(jīng)創(chuàng)建了一個測試類,讓我們使用 Ant 來將它集成到構(gòu)建中。





    回頁首


    使用 Ant 將測試集成到構(gòu)建中

    Jakarta Project 將 Ant 工具說成“不帶 make 缺點的 make”。Ant 正在成為開放源代碼世界中實際上的標(biāo)準(zhǔn)。原因很簡單:Ant 是使用 Java 語言編寫的,這種語言可以讓構(gòu)建過程在多種平臺上使用。這種特性簡化了在不同 OS 平臺之間的程序員的合作,而合作是開放源代碼社區(qū)的一種需要。您可以在自己選擇的平臺上進行開發(fā) 構(gòu)建。Ant 的特性包括:

    • 類可擴展性 Java 類可用于擴展構(gòu)建特性,而不必使用基于 shell 的命令。
    • 開放源代碼 因為 Ant 是開放源代碼,因此類擴展示例很充足。我發(fā)現(xiàn)通過示例來學(xué)習(xí)非常棒。
    • XML 可配置 Ant 不僅是基于 Java 的,它還使用 XML 文件配置構(gòu)建過程。假設(shè)構(gòu)建實際上是分層的,那么使用 XML 描述 make 過程就是其邏輯層。另外,如果您了解 XML,要學(xué)習(xí)如何配置構(gòu)建就更簡單一些。

    圖 2 簡要介紹了一個配置文件。配置文件由目標(biāo)樹構(gòu)成。每個目標(biāo)都包含了要執(zhí)行的任務(wù),其中任務(wù)就是可以執(zhí)行的代碼。在本例中, mkdir是目標(biāo) compile的任務(wù)。 mkdir是建立在 Ant 中的一個任務(wù),用于創(chuàng)建目錄。 Ant 帶有一套健全的內(nèi)置任務(wù)。您也可以通過擴展 Ant 任務(wù)類來添加自己的功能。

    每個目標(biāo)都有唯一的名稱和可選的相關(guān)性。目標(biāo)相關(guān)性需要在執(zhí)行目標(biāo)任務(wù)列表之前執(zhí)行。例如圖 2 所示,在執(zhí)行 compile 目標(biāo)中的任務(wù)之前需要先運行 JUNIT 目標(biāo)。這種類型的配置可以讓您在一個配置中有多個樹。



    圖 2. Ant XML 構(gòu)建圖
    Ant xml 構(gòu)建圖

    與經(jīng)典 make 實用程序的相似性是非常顯著的。這是理所當(dāng)然的,因為 make 就是 make。但也要記住有一些差異:通過 Java 實現(xiàn)的跨平臺和可擴展性,通過 XML 實現(xiàn)的可配置,還有開放源代碼。

    下載和安裝 Ant
    首先下載 Ant(請參閱 參考資料 )。將 Ant 解壓縮到 tools 目錄,再將 Ant bin 目錄添加到路徑中。(在我的機器上是 e:\tools\ant\bin 。)設(shè)置 ANT_HOME 環(huán)境變量。在 NT 中,這意味著進入系統(tǒng)屬性,然后以帶有值的變量形式添加 ANT_HOME。ANT_HOME 應(yīng)該設(shè)置為 Ant 根目錄,即包含 binlib 目錄的目錄。(對我來說,是 e:\tools\ant 。)確保 JAVA_HOME 環(huán)境變量設(shè)置為安裝了 JDK 的目錄。Ant 文檔有關(guān)于安裝的詳細(xì)信息。

    下載和安裝 JUnit
    下載 JUnit 3.2(請參閱 參考資料 )。 解開 junit.zip ,并將 junit.jar 添加到 CLASSPATH。如果將 junit.zip 解包到類路徑中,可以通過運行以下命令來測試安裝: java junit.textui.TestRunner junit.samples.AllTests

    定義目錄結(jié)構(gòu)
    在開始我們的構(gòu)建和測試過程之前,需要一個項目布局。圖 3 顯示了我的樣本項目的布局。下面描述了布局的目錄結(jié)構(gòu):

    • build -- 類文件的臨時構(gòu)建位置。構(gòu)建過程將創(chuàng)建這個目錄。
    • src -- 源代碼的位置。 Src 被分為 test 文件夾和 main 文件夾,前者用于所有的測試代碼,而后者包含可交付的代碼。將測試代碼與主要代碼分離提供了幾點特性。首先,使主要代碼中的混亂減少。其次,它允許包對 齊。我就熱衷與將類和與其相關(guān)的包放置在一起。測試就應(yīng)該和測試在一起。它還有助于分發(fā)過程,因為你不可能打算將單元測試分發(fā)給客戶。

    在實際中,我們有多個目錄,例如 distributiondocumentation 。我們還會在 main 下有多個用于包的目錄,例如 com.company.util

    因為目錄結(jié)構(gòu)經(jīng)常變動,所以在 build.xml 中有這些變動的全局字符串常數(shù)是很重要的。



    圖 3. 項目布局圖
    項目布局圖

    Ant 構(gòu)建配置文件示例
    下一步,我們要創(chuàng)建配置文件。清單 4 顯示了一個 Ant 構(gòu)建文件示例。構(gòu)建文件中的關(guān)鍵就是名為 runtests 的目標(biāo)。這個目標(biāo)進行分支判斷并運行外部程序,其中外部程序是前面已安裝的 junit.textui.TestRunner 。我們指定要使用語句 test.com.company.AllJUnitTests 來運行哪個測試套件。



    清單 4. 構(gòu)建文件示例
        <property name="app.name"   value="sample" />
    <property name="build.dir" value="build/classes" />
    <target name="JUNIT">
    <available property="junit.present" classname="junit.framework.TestCase" />
    </target>
    <target name="compile" depends="JUNIT">
    <mkdir dir="${build.dir}"/>
    <javac srcdir="src/main/" destdir="${build.dir}" >
    <include name="**/*.java"/>
    </javac>
    </target>
    <target name="jar" depends="compile">
    <mkdir dir="build/lib"/>
    <jar jarfile="build/lib/${app.name}.jar"
    basedir="${build.dir}" includes="com/**"/>
    </target>
    <target name="compiletests" depends="jar">
    <mkdir dir="build/testcases"/>
    <javac srcdir="src/test" destdir="build/testcases">
    <classpath>
    <pathelement location="build/lib/${app.name}.jar" />
    <pathelement path="" />
    </classpath>
    <include name="**/*.java"/>
    </javac>
    </target>
    <target name="runtests" depends="compiletests" if="junit.present">
    <java fork="yes" classname="junit.textui.TestRunner"
    taskname="junit" failonerror="true">
    <arg value="test.com.company.AllJUnitTests"/>
    <classpath>
    <pathelement location="build/lib/${app.name}.jar" />
    <pathelement location="build/testcases" />
    <pathelement path="" />
    <pathelement path="${java.class.path}" />
    </classpath>
    </java>
    </target>
    </project>

    運行 Ant 構(gòu)建示例
    開發(fā)過程中的下一步是運行將創(chuàng)建和測試 HelloWorld 類的構(gòu)建。清單 5 顯示了構(gòu)建的結(jié)果,其中包括了各個目標(biāo)部分。最酷的那部分是 runtests 輸出語句:它告訴我們整個測試套件都正確運行了。

    我在圖 4 和圖 5 中顯示了 JUnit GUI,其中所要做的就是將 runtest 目標(biāo)從 junit.textui.TestRunner 改為 junit.ui.TestRunner 。當(dāng)您使用 JUnit 的 GUI 部分時,您必須選擇退出按鈕來繼續(xù)構(gòu)建過程。如果使用 Junit GUI 構(gòu)建包,那么它將更難與大型的構(gòu)建過程相集成。另外,文本輸出也與構(gòu)建過程更一致,并可以定向輸出到一個用于主構(gòu)建記錄的文本文件。這對于每天晚上都要進 行的構(gòu)建非常合適。



    清單 5. 構(gòu)建輸出示例
    E:\projects\sample>ant runtests
    Searching for build.xml ...
    Buildfile: E:\projects\sample\build.xml
    JUNIT:
    compile:
    [mkdir] Created dir: E:\projects\sample\build\classes
    [javac] Compiling 1 source file to E:\projects\sample\build\classes
    jar:
    [mkdir] Created dir: E:\projects\sample\build\lib
    [jar] Building jar: E:\projects\sample\build\lib\sample.jar
    compiletests:
    [mkdir] Created dir: E:\projects\sample\build\testcases
    [javac] Compiling 3 source files to E:\projects\sample\build\testcases
    runtests:
    [junit] ..
    [junit] Time: 0.031
    [junit]
    [junit] OK (2 tests)
    [junit]
    BUILD SUCCESSFUL
    Total time: 1 second



    圖 4. JUnit GUI 測試成功
    JUnit gui 測試成功


    圖 5. JUnit GUI 測試失敗
    JUnit gui 測試失敗




    回頁首


    了解測試的工作原理

    讓我們搞點破壞,然后看看會發(fā)生什么事。夜深了,我們決定把 "Hello World" 變成一個靜態(tài)字符串。在更改期間,我們 不小心打錯了字母,將 "o" 變成了 "0",如清單 6 所示。



    清單 6. Hello world 類更改
    package com.company;
    public class HelloWorld {
    private final static String HELLO_WORLD = "Hell0 World";
    public String sayHello() {
    return HELLO_WORLD;
    }
    }

    在構(gòu)建包時,我們看到了錯誤。清單 7 顯示了 runtest 中的錯誤。它顯示了失敗的測試類和測試方法,并說明了為什么會失敗。我們返回到代碼中,改正錯誤后離開。



    清單 7. 構(gòu)建錯誤示例
    E:\projects\sample>ant runtests
    Searching for build.xml ...
    Buildfile: E:\projects\sample\build.xml
    JUNIT:
    compile:
    jar:
    compiletests:
    runtests:
    [junit] ..F
    [junit] Time: 0
    [junit]
    [junit] FAILURES!!!
    [junit] Test Results:
    [junit] Run: 2 Failures: 1 Errors: 0
    [junit] There was 1 failure:
    [junit] 1) testSayHello(test.com.company.HelloWorldTest) "expected:<Hello
    World> but was:<Hell0 World>"
    [junit]
    BUILD FAILED
    E:\projects\sample\build.xml:35: Java returned: -1
    Total time: 0 seconds





    回頁首


    并非完全無痛

    新的過程并不是完全無痛的。為使單元測試成為開發(fā)的一部分,您必須采取以下幾個步驟:

    1. 下載和安裝 JUnit。
    2. 下載和安裝 Ant。
    3. 為構(gòu)建創(chuàng)建單獨的結(jié)構(gòu)。
    4. 實現(xiàn)與主類分開的測試類。
    5. 學(xué)習(xí) Ant 構(gòu)建過程。

    但好處遠(yuǎn)遠(yuǎn)超過了痛苦。通過使單元測試成為開發(fā)過程的一部分,您可以:

    • 自動驗證以捕捉更改“臭蟲”
    • 從接口角度設(shè)計類
    • 提供干凈的示例
    • 在發(fā)行包中避免代碼混亂和類膨脹。




    回頁首


    實現(xiàn) 24x7

    保證產(chǎn)品的質(zhì)量要花費很多錢,但如果質(zhì)量有缺陷,花費的錢就更多。如何才能使所花的錢獲得最大價值,來保證產(chǎn)品質(zhì)量呢?

    • 評審設(shè)計和代碼。 評審可以達到的效果是單純測試的一半。
    • 通過單元測試來確認(rèn)模塊可以使用。 盡管測試早就存在,但隨著開發(fā)實踐的不斷發(fā)展,單元測試逐漸成為日常開發(fā)過程的一個部分。

    在 我 10 年的開發(fā)生涯里,為 emageon.com 工作是最重要的部分之一。在 emageon.com 時,設(shè)計評審、代碼評審和單元測試是每天都要做的事。這種日常開發(fā)習(xí)慣造就了最高質(zhì)量的產(chǎn)品。軟件在客戶地點第一年的當(dāng)機次數(shù)為零,是一個真正的 24x7 產(chǎn)品。單元測試就象刷牙:您不一定要做,但如果做了,生活質(zhì)量就更好。



    參考資料



    關(guān)于作者


    Malcolm G. Davis 擁有自己的咨詢公司,并任公司的總裁,該公司位于美國阿拉巴馬州的伯明翰 (Birmingham)。他把自己看做是個 Java 傳道者。在工作之余,他喜歡跑步,以及和他的孩子們一起玩耍。您可以通過 malcolm@nuearth.com 與 Malcolm 聯(lián)系。

    posted on 2007-04-19 14:52 wqwqwqwqwq 閱讀(475) 評論(0)  編輯  收藏 所屬分類: IBM Tech
    <2007年4月>
    25262728293031
    1234567
    891011121314
    15161718192021
    22232425262728
    293012345




    常用鏈接

    留言簿(10)

    隨筆分類(95)

    隨筆檔案(97)

    文章檔案(10)

    相冊

    J2ME技術(shù)網(wǎng)站

    java技術(shù)相關(guān)

    mess

    搜索

    •  

    最新評論

    閱讀排行榜

    校園夢網(wǎng)網(wǎng)絡(luò)電話,中國最優(yōu)秀的網(wǎng)絡(luò)電話
    主站蜘蛛池模板: 一个人看的www在线免费视频| 久久免费美女视频| 亚洲日韩国产精品乱| 久久99国产乱子伦精品免费| 亚洲 欧洲 自拍 另类 校园| 在线观看午夜亚洲一区| 黄网站色在线视频免费观看| 人妻巨大乳hd免费看| 亚洲激情视频网站| 国产成人毛片亚洲精品| 亚洲黄色免费网址| caoporn国产精品免费| 精品亚洲国产成人| 国产成A人亚洲精V品无码性色| 猫咪社区免费资源在线观看 | 久久综合日韩亚洲精品色| 在线看片人成视频免费无遮挡| 中文在线日本免费永久18近| 中文字幕亚洲综合小综合在线| 中文字幕久久亚洲一区| 免费无码又爽又高潮视频| 久久精品中文字幕免费| 免费看黄福利app导航看一下黄色录像 | 国产高清视频免费在线观看| 亚洲午夜精品国产电影在线观看| 亚洲精品NV久久久久久久久久| 黄色永久免费网站| 毛片在线全部免费观看| 日韩在线观看免费完整版视频| 国产AV旡码专区亚洲AV苍井空| 亚洲无线电影官网| 自拍偷自拍亚洲精品被多人伦好爽 | 91亚洲性爱在线视频| 亚洲色偷拍另类无码专区| 四虎免费影院4hu永久免费| 亚洲成在人线aⅴ免费毛片| 久久久久成人片免费观看蜜芽| 人人爽人人爽人人片av免费| 亚洲精品无AMM毛片| 2020国产精品亚洲综合网| 亚洲欧洲日本国产|