導航:首頁 > 編程語言 > java單元測試用例模板

java單元測試用例模板

發布時間:2024-11-26 13:02:33

① 如何使用junit4寫單元測試用例

我們在哪歷編寫大型程序的時候,需要寫成千上萬個 方法或函數,這些函數的功能可能很強大,但我們在程序中只用到該函數的一小部分功能,並且經過調試可以確定,這一小部分功能是正確的。但是,我們同時應該 確保每一個函數都完全正確,因為如果我們今後如果對程序進行擴展,用到了某個函數的其他功能,而這個功能有bug的話,那絕對是一件非常郁悶的事情。所以 說,每編寫完一個函數之後,都應該對這個函數的方方面面進行測試,這樣凳櫻的測試我們稱之為單元測試。傳統的編程方式,進行單元測試是一件很麻煩的事情,你要 重新寫另外一個程序,在該程序中調用你需要測試的方法,並且仔細觀察運行結果,看看是否有錯。正因為如此麻煩,所以程序員們編寫單元測試的熱情不是很高。 於是有一個牛人推出了單元測試包,大大簡化了進行單元測試所要做的工作,這就是JUnit4。本文簡要介紹一下在Eclipse3.2中使用JUnit4 進行單元測試的方法。
首先,我們來一個傻瓜式速成教程,不要問為什麼,Follow Me,先來體驗一下單元測試的快棗緩叢感!
首先新建一個項目叫JUnit_Test,我們編寫一個Calculator類,這是一個能夠簡單實現加減乘除、平方、開方的計算器類,然後對這些功能進行單元測試。這個類並不是很完美,我們故意保留了一些Bug用於演示,這些Bug在注釋中都有說明。該類代碼如下:
package andycpp;
public class Calculator …{
private static int result; // 靜態變數,用於存儲運行結果
public void add(int n) …{
result = result + n;
}
public void substract(int n) …{
result = result – 1; //Bug: 正確的應該是 result =result-n
}
public void multiply(int n) …{
} // 此方法尚未寫好
public void divide(int n) …{
result = result / n;
}
public void square(int n) …{
result = n * n;
}
public void squareRoot(int n) …{
for (;;); //Bug : 死循環
}
public void clear() …{ // 將結果清零
result = 0;
}
public int getResult() …{
return result;
}
}
第二步,將JUnit4單元測試包引入這個項目:在該項目上點右鍵,點「屬性」,
在彈出的屬性窗口中,首先在左邊選擇「java Build Path」,然後到右上選擇「Libraries」標簽,之後在最右邊點擊「Add Library…」按鈕,如下圖所示:

然後在新彈出的對話框中選擇JUnit4並點擊確定,如上圖所示,JUnit4軟體包就被包含進我們這個項目了。
第三步,生成JUnit測試框架:在Eclipse的Package Explorer中用右鍵點擊該類彈出菜單,選擇「New à JUnit Test Case」。

在彈出的對話框中,進行相應的選擇

點擊「下一步」後,系統會自動列出你這個類中包含的方法,選擇你要進行測試的方法。此例中,我們僅對「加、減、乘、除」四個方法進行測試。
之後系統會自動生成一個新類CalculatorTest,裡麵包含一些空的測試用例。你只需要將這些測試用例稍作修改即可使用。完整的CalculatorTest代碼如下:
package andycpp;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class CalculatorTest …{
private static Calculator calculator = new Calculator();
@Before
public void setUp() throws Exception …{
calculator.clear();
}
@Test
public void testAdd() …{
calculator.add(2);
calculator.add(3);
assertEquals(5, calculator.getResult());
}

@Test
public void testSubstract() …{
calculator.add(10);
calculator.substract(2);
assertEquals(8, calculator.getResult());
}
@Ignore(「Multiply() Not yet implemented」)
@Test
public void testMultiply() …{
}
@Test
public void testDivide() …{
calculator.add(8);
calculator.divide(2);
assertEquals(4, calculator.getResult());
}
}
第四步,運行測試代碼:按照上述代碼修改完畢後,我們在CalculatorTest類上點右鍵,選擇「Run As à JUnit Test」來運行我們的測試,如下圖所示:

運行結果如下:
進度條是紅顏色表示發現錯誤,具體的測試結果在進度條上面有表示「共進行了4個測試,其中1個測試被忽略,一個測試失敗」
至此,我們已經完整體驗了在Eclipse中使用JUnit的方法。在接下來的文章中,我會詳細解釋測試代碼中的每一個細節!

② 單元測試的測試用例

下面說說測試用例、輸入數據及預期輸出。輸入數據是測試用例的核心,對輸入數據的定義是:被測試函數所讀取的外部數據及這些數據的初始值。外部數據是對於被測試函數來說的,實際上就是除了局部變數以外的其他數據,老納把這些數據分為幾類:參數、成員變數、全局變數、IO媒體。IO媒體是指文件資料庫或其他儲存或傳輸數據的媒體,例如,被測試函數要從文件或資料庫讀取數據,那麼,文件或資料庫中的原始數據也屬於輸入數據。一個函數無論多復雜,都無非是對這幾類數據的讀取、計算和寫入。預期輸出是指:返回值及被測試函數所寫入的外部數據的結果值。返回值就不用說了,被測試函數進行了寫操作的參數(輸出參數)、成員變數、全局變數、IO媒體,它們的預期的結果值都是預期輸出。一個測試用例,就是設定輸入數據,運行被測試函數,然後判斷實際輸出是否符合預期。下面舉一個與成員變數有關的例子: void CMyClass::Grow(int years)
{
mAge += years;
if(mAge < 10)
mPhase = 兒童;
else if(mAge <20)
mPhase = 少年;
else if(mAge <45)
mPhase = 青年;
else if(mAge <60)
mPhase = 中年;
else
mPhase = 老年;
}
測試函數中的一個測試用例:
CaseBegin();{
int years = 1;
pObj->mAge = 8;
pObj->Grow(years);
ASSERT( pObj->mAge == 9 );
ASSERT( pObj->mPhase == 兒童 );
}CaseEnd();
在輸入數據中對被測試類的成員變數mAge進行賦值,在預期輸出中斷言成員變數的值。現在可以看到老納所推薦的格式的好處了吧,這種格式可以適應很復雜的測試。在輸入數據部分還可以調用其他成員函數,例如:執行被測試函數前可能需要讀取文件中的數據保存到成員變數,或需要連接資料庫,老納把這些操作稱為初始化操作。例如,上例中 ASSERT( ...)之前可以加pObj->OpenFile();。為了訪問私有成員,可以將測試類定義為產品類的友元類。例如,定義一個宏:
#define UNIT_TEST(cls) friend class cls##Tester;
然後在產品類聲明中加一行代碼:UNIT_TEST(ClassName)。 下面談談測試用例設計。前面已經說了,測試用例的核心是輸入數據。預期輸出是依據輸入數據和程序功能來確定的,也就是說,對於某一程序,輸入數據確定了,預期輸出也就可以確定了,至於生成/銷毀被測試對象和運行測試的語句,是所有測試用例都大同小異的,因此,我們討論測試用例時,只討論輸入數據。
前面說過,輸入數據包括四類:參數、成員變數、全局變數、IO媒體,這四類數據中,只要所測試的程序需要執行讀操作的,就要設定其初始值,其中,前兩類比較常用,後兩類較少用。顯然,把輸入數據的所有可能取值都進行測試,是不可能也是無意義的,我們應該用一定的規則選擇有代表性的數據作為輸入數據,主要有三種:正常輸入,邊界輸入,非法輸入,每種輸入還可以分類,也就是平常說的等價類法,每類取一個數據作為輸入數據,如果測試通過,可以肯定同類的其他輸入也是可以通過的。下面舉例說明:
正常輸入
例如字元串的Trim函數,功能是將字元串前後的空格去除,那麼正常的輸入可以有四類:前面有空格;後面有空格;前後均有空格;前後均無空格。
邊界輸入
上例中空字元串可以看作是邊界輸入。
再如一個表示年齡的參數,它的有效范圍是0-100,那麼邊界輸入有兩個:0和100。
非法輸入
非法輸入是正常取值范圍以外的數據,或使代碼不能完成正常功能的輸入,如上例中表示年齡的參數,小於0或大於100都是非法輸入,再如一個進行文件操作的函數,非法輸入有這么幾類:文件不存在;目錄不存在;文件正在被其他程序打開;許可權錯誤。
如果函數使用了外部數據,則正常輸入是肯定會有的,而邊界輸入和非法輸入不是所有函數都有。一般情況下,即使沒有設計文檔,考慮以上三種輸入也可以找出函數的基本功能點。實際上,單元測試與代碼編寫是「一體兩面」的關系,編碼時對上述三種輸入都是必須考慮的,否則代碼的健壯性就會成問題。
白盒覆蓋
上面所說的測試數據都是針對程序的功能來設計的,就是所謂的黑盒測試。單元測試還需要從另一個角度來設計測試數據,即針對程序的邏輯結構來設計測試用例,就是所謂的白盒測試。在老納看來,如果黑盒測試是足夠充分的,那麼白盒測試就沒有必要,可惜「足夠充分」只是一種理想狀態,例如:真的是所有功能點都測試了嗎?程序的功能點是人為的定義,常常是不全面的;各個輸入數據之間,有些組合可能會產生問題,怎樣保證這些組合都經過了測試?難於衡量測試的完整性是黑盒測試的主要缺陷,而白盒測試恰恰具有易於衡量測試完整性的優點,兩者之間具有極好的互補性,例如:完成功能測試後統計語句覆蓋率,如果語句覆蓋未完成,很可能是未覆蓋的語句所對應的功能點未測試。
白盒測試針對程序的邏輯結構設計測試用例,用邏輯覆蓋率來衡量測試的完整性。邏輯單位主要有:語句、分支、條件、條件值、條件值組合,路徑。語句覆蓋就是覆蓋所有的語句,其他類推。另外還有一種判定條件覆蓋,其實是分支覆蓋與條件覆蓋的組合,在此不作討論。跟條件有關的覆蓋就有三種,解釋一下:條件覆蓋是指覆蓋所有的條件表達式,即所有的條件表達式都至少計算一次,不考慮計算結果;條件值覆蓋是指覆蓋條件的所有可能取值,即每個條件的取真值和取假值都要至少計算一次;條件值組合覆蓋是指覆蓋所有條件取值的所有可能組合。老納做過一些粗淺的研究,發現與條件直接有關的錯誤主要是邏輯操作符錯誤,例如:||寫成&&,漏了寫!什麼的,採用分支覆蓋與條件覆蓋的組合,基本上可以發現這些錯誤,另一方面,條件值覆蓋與條件值組合覆蓋往往需要大量的測試用例,因此,在老納看來,條件值覆蓋和條件值組合覆蓋的效費比偏低。效費比較高且完整性也足夠的測試要求是這樣的:完成功能測試,完成語句覆蓋、條件覆蓋、分支覆蓋、路徑覆蓋。做過單元測試的朋友恐怕會對老納提出的測試要求給予一個字的評價:暈!或者兩個字的評價:狂暈!因為這似乎是不可能的要求,要達到這種測試完整性,其測試成本是不可想像的,不過,出家人不打逛語,老納之所以提出這種測試要求,是因為利用一些工具,可以在較低的成本下達到這種測試要求,後面將會作進一步介紹。
關於白盒測試用例的設計,程序測試領域的書籍一般都有講述,普通方法是畫出程序的邏輯結構圖如程序流程圖或控制流圖,根據邏輯結構圖設計測試用例,這些是純粹的白盒測試,不是老納想推薦的方式。老納所推薦的方法是:先完成黑盒測試,然後統計白盒覆蓋率,針對未覆蓋的邏輯單位設計測試用例覆蓋它,例如,先檢查是否有語句未覆蓋,有的話設計測試用例覆蓋它,然後用同樣方法完成條件覆蓋、分支覆蓋和路徑覆蓋,這樣的話,既檢驗了黑盒測試的完整性,又避免了重復的工作,用較少的時間成本達到非常高的測試完整性。不過,這些工作可不是手工能完成的,必須藉助於工具,後面會介紹可以完成這些工作的測試工具。

③ 寫好單元測試的8個小技巧,一文總結!

當測試驅動開發在業界愈發流行,對單元測試的要求也在提升。以下是一些編寫高效單元測試的實用技巧:



以上這些小技巧能幫助你編寫更有效的單元測試,提升軟體開發的質量和效率。

④ 在項目中怎麼用junit寫單元測試用例

首先我們需要先下載相應的 JUnit 相關的 JAR 包,下載的過程可以去 JUnit 的官方網站,也可以直接通過 Maven 資源倉庫來完成。

使用簡單的 @Test 註解實現我們的測試方法的編寫和執行
准備工作做好之後,接下來我們就可以開始嘗試編寫壹個簡單的測試代碼了。首先,我們編寫了壹個 Calculator 類,並提供五個方法分別完成加減乘除以及求平方的運算。代碼如下:

package net.oschina.rrfhoinn.main;
public class Calculator {
public void add(int n){
result += n;
}
public void substract(int n){
result -= n;
}
public void multiply(int n){
result *= n;
}
public void divide(int n){
result /= n;
}
public void square(int n){
result = n * n;
}
public int getReuslt(){
return result;
}
public void clear(){
result = 0;
}
private static int result;
}


在測試類中用到了JUnit4框架,自然要把相應地Package包含進來。最主要地一個Package就是org.junit.*。把它包含進來之後,絕大部分功能就有了。還有一句話也非常地重要「import static org.junit.Assert.*;」,我們在測試的時候使用的壹系列assertEquals()方法就來自這個包。大家注意壹下,這是壹個靜態包含(static),是JDK5中新增添的壹個功能。也就是說,assertEquals是Assert類中的壹系列的靜態方法,壹般的使用方式是Assert. assertEquals(),但是使用了靜態包含後,前面的類名就可以省略了,使用起來更加的方便。
另外要注意的是,我們的測試類是壹個獨立的類,沒有任何父類。測試類的名字也可以任意命名,沒有任何局限性。所以我們不能通過類的聲明來判斷它是不是一個測試類,它與普通類的區別在於它內部的方法的聲明,我們接著會講到。在測試類中,並不是每壹個方法都是用於測試的,所以我們必須使用「註解」來明確表明哪些是測試方法。「註解」也是JDK5的壹個新特性,用在此處非常恰當。我們可以看到,在某些方法的前有@Before、@Test、@Ignore等字樣,這些就是註解,以壹個「@」作為開頭。這些註解都是JUnit4自定義的,熟練掌握這些註解的含義,對於編寫恰當的測試類非常重要。

接下來我們創建壹個測試類 CalculatorTest.java,代碼如下:

package net.oschina.rrfhoinn.test;
import static org.junit.Assert.*;
import org.junit.Test;
import net.oschina.rrfhoinn.main.Calculator;
public class CalculatorTest {
private static Calculator calculator = new Calculator();
@Test
public void testAdd(){
calculator.add(7);
calculator.add(8);
assertEquals(15, calculator.getReuslt());
}
}


首先,我們要在方法的前面使用@Test標注,以表明這是壹個測試方法。對於方法的聲明也有如下要求:名字可以隨便取,沒有任何限制,但是返回值必須為void,而且不能有任何參數。如果違反這些規定,會在運行時拋出壹個異常。至於方法內該寫些什麼,那就要看你需要測試些什麼了。比如上述代碼中,我們想測試壹下add()方法的功能是否正確,就在測試方法中調用幾次add函數,初始值為0,先加7,再加8,我們期待的結果應該是15。如果最終實際結果也是15,則說明add()方法是正確的,反之說明它是錯的。assertEquals(15, calculator.getResult());就是用來判斷期待結果和實際結果是否相等,其中第壹個參數填寫期待結果,第二個參數填寫實際結果,也就是通過計算得到的結果。這樣寫好之後,JUnit 會自動進行測試並把測試結果反饋給用戶。
如果想運行它,可以在 eclipse 的資源管理器中選擇該類文件,然後點擊右鍵,選擇 Run As->JUnit Test 即可看到運行結果。

使用@Test 的屬性 Ignore 指定測試時跳過這個方法
如果在寫程序前做了很好的規劃,那麼哪些方法是什麼功能都應該實現並且確定下來。因此,即使該方法尚未完成,他的具體功能也是確定的,這也就意味著你可以為他編寫測試用例。但是,如果你已經把該方法的測試用例寫完,但該方法尚未完成,那麼測試的時候無疑是「失敗」。這種失敗和真正的失敗是有區別的,因此 JUnit 提供了壹種方法來區別他們,那就是在這種測試函數的前面加上 @Ignore 標注,這個標注的含義就是「某些方法尚未完成,暫不參與此次測試」。這樣的話測試結果就會提示你有幾個測試被忽略,而不是失敗。壹旦你完成了相應函數,只需要把@Ignore標注刪去,就可以進行正常的測試。
比如說上面的測試類 Calculator.java 中,假設我們的 Calculator 類的 multiply() 方法沒有實現,我們可以在測試類 CalculatorTest 中先寫如下測試代碼:

package net.oschina.rrfhoinn.test;
import static org.junit.Assert.*;
import org.junit.Ignore;
import org.junit.Test;
import net.oschina.rrfhoinn.main.Calculator;
public class CalculatorTest {
private static Calculator calculator = new Calculator();
... //此處代碼省略
@Ignore("method square() not implemented, please test this later...")
@Test
public void testSquare(){
calculator.square(3);
assertEquals(9, calculator.getReuslt());
}
}


我們再運行壹次測試,會看到如下結果,從圖中可以很明顯的看出,方法testSquare() 上的 @Ignore 註解已經生效了,運行時直接跳過了它,而方法testAdd()仍然正常的運行並通過了測試。

使用註解 @Before 和 @After 來完成前置工作和後置工作
前置工作通常是指我們的測試方法在運行之前需要做的壹些准備工作,如資料庫的連接、文件的載入、輸入數據的准備等需要在運行測試方法之前做的事情,都屬於前置工作;類似的,後置工作則是指測試方法在運行之後的壹些要做的事情,如釋放資料庫連接、輸入輸出流的關閉等;比如我們上面的測試,由於只聲明了壹個 Calculator 對象,他的初始值是0,但是測試完加法操作後,他的值就不是0了;接下來測試減法操作,就必然要考慮上次加法操作的結果。這絕對是壹個很糟糕的設計!我們非常希望每壹個測試方法都是獨立的,相互之間沒有任何耦合度。因此,我們就很有必要在執行每壹個測試方法之前,對Calculator對象進行壹個「復原」操作,以消除其他測試造成的影響。因此,「在任何壹個測試方法執行之前必須執行的代碼」就是壹個前置工作,我們用註解 @Before 來標注它,如下例子所示:

package net.oschina.rrfhoinn.test;
...
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class CalculatorTest {
...//這里省略部分代碼
@Before
public void setUp() throws Exception {
calculator.clear();
}
@After
public void tearDown() throws Exception {
System.out.println("will do sth here...");
}
...//這里省略部分代碼
}


另外要說的是,註解 @Before 是定義在 org.junit.Before 這個類中的,因此使用時需要將其引入我們的代碼中。這樣做了之後,每次我們運行測試方法時,JUnit 都會先運行 setUp() 方法將 result 的值清零。不過要注意的是,這里不再需要 @Test 註解,因為這並不是壹個 test,只是壹個前置工作。同理,如果「在任何測試執行之後需要進行的收尾工作,我們應該使用 @After 來標注,方法與它類似。由於本例比較簡單,不需要用到此功能,所以我們只是簡單了給它添加了壹個 tearDown() 方法並在收尾時列印壹句話到控制台,並且使用 @After 來註解這個方法。
使用@BeforeClass 和 @AfterClass 來完成只需要執行壹次的前置工作和後置工作
上面我們提到了兩個註解 @Before 和 @After ,我們來看看他們是否適合完成如下功能:有壹個類負責對大文件(超過500 MB)進行讀寫,他的每壹個方法都是對文件進行操作。換句話說,在調用每壹個方法之前,我們都要打開壹個大文件並讀入文件內容,這絕對是壹個非常耗費時的操作。如果我們使用 @Before 和 @After ,那麼每次測試都要讀取壹次文件,效率及其低下。所以我們希望的是,在所有測試壹開始讀壹次文件,所有測試結束之後釋放文件,而不是每次測試都讀文件。JUnit的作者顯然也考慮到了這個問題,它給出了@BeforeClass 和 @AfterClass 兩個註解來幫我們實現這個功能。從名字上就可以看出,用這兩個註解標注的函數,只在測試用例初始化時執行 @BeforeClass 方法,當所有測試執行完畢之後,執行 @AfterClass 進行收尾工作。在這里要注意壹下,每個測試類只能有壹個方法被標注為 @BeforeClass 或 @AfterClass,而且該方法必須是 public static 類型的。
使用@Test 的屬性 timeout 來完成限時測試,以檢測代碼中的死循環
現在假設我們的 Calculator 類中的 square() 方法是個死循環,那應該怎麼辦呢,比如說像下面這樣:

public void square(int n){
for(;;){}
}


如果測試的時候遇到死循環,你的臉上絕對不會露出笑容的。因此,對於那些邏輯很復雜,循環嵌套比較深的、有可能出現死循環的程序,因此壹定要採取壹些預防措施。限時測試是壹個很好的解決方案。我們給這些測試函數設定壹個預期的執行時間,超過了這壹時間,他們就會被系統強行終止,並且系統還會向你匯報該函數結束的原因是因為超時,這樣你就可以發現這些 Bug 了。要實現這壹功能,只需要給 @Test 標注加壹個參數timeout即可,代碼如下:

@Test(timeout=2000L)
public void testSquare() {
calculator.square(3);
assertEquals(9, calculator.getReuslt());
}


timeout參數表明了你預計該方法運行的時長,單位為毫秒,因此2000就代表2秒。現在我們讓這個測試方法運行壹下,看看失敗時是什麼效果。

使用@Test 的屬性expected來監控測試方法中可能會拋出的某些異常
JAVA中的異常處理也是壹個重點,因此你經常會編寫壹些需要拋出異常的函數。如果你覺得壹個函數應該拋出異常,但是它沒拋出,這算不算 Bug 呢?這當然是Bug,JUnit 也考慮到了這壹點,並且可以幫助我們找到這種 Bug。例如,我們寫的計算器類有除法功能,如果除數是壹個0,那麼必然要拋出「除0異常」。因此,我們很有必要對這些進行測試。代碼如下:

@Test(expected=java.lang.ArithmeticException.class)
public void testDivide(){
calculator.divide(0);
}


如上述代碼所示,我們需要使用@Test註解中的expected屬性,將我們要檢驗的異常(這里是 java.lang.ArithmeticException)傳遞給他,這樣 JUnit 框架就能自動幫我們檢測是否拋出了我們指定的異常。
指定 JUnit 運行測試用例時的 Runner
大家有沒有想過這個問題,當你把測試代碼提交給JUnit框架後,框架是如何來運行你的代碼的呢?答案就是Runner。在JUnit中有很多個Runner,他們負責調用你的測試代碼,每壹個Runner都有其各自的特殊功能,你要根據需要選擇不同的Runner來運行你的測試代碼。可能你會覺得奇怪,前面我們寫了那麼多測試,並沒有明確指定壹個Runner啊?這是因為JUnit中有壹個默認的Runner,如果你沒有指定,那麼系統會自動使用默認Runner來運行你的代碼。換句話說,下面兩段代碼含義是完全壹樣的:

import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class CalculatorTest {
...//省略此處代碼
}
//用了系統默認的JUnit4.class,運行效果完全壹樣
public class CalculatorTest {
...//省略此處代碼
}

閱讀全文

與java單元測試用例模板相關的資料

熱點內容
編程時如何設置刀補指令和刀補號 瀏覽:18
dvd文件怎樣轉存u盤 瀏覽:972
策劃旅遊線路app需要哪些點 瀏覽:28
samba設置文件夾許可權設置密碼 瀏覽:100
列印的pdf文件在哪裡查找 瀏覽:267
java性能優化視頻 瀏覽:931
u盤文件刪了又出現 瀏覽:601
哪些行業需要網路機櫃 瀏覽:155
汽車線路圖數據怎麼查 瀏覽:798
資料庫句柄無效 瀏覽:121
微信後台數據在哪個文件夾 瀏覽:765
infues找不到文件 瀏覽:226
什麼是國盾網站 瀏覽:375
錯誤1325短文件名 瀏覽:663
ubuntu文件映射 瀏覽:885
手機中百度地圖文件夾名稱是什麼 瀏覽:476
劍靈門派沒有升級界面 瀏覽:499
有哪些機甲網路游戲 瀏覽:502
java單元測試用例模板 瀏覽:699
哪些地區覆蓋了無極網路 瀏覽:825

友情鏈接