国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Spring基于注解TestContext 測試框架使用詳解

2019-11-08 03:25:36
字體:
來源:轉載
供稿:網友
原創整理不易,轉載請注明出處:SPRing基于注解TestContext 測試框架使用詳解

代碼下載地址:http://www.zuidaima.com/share/1775574182939648.htm

概述

spring 2.5 相比于 Spring 2.0 所新增的最重要的功能可以歸結為以下 3 點:

基于注解的 IoC 功能;基于注解驅動的 Spring MVC 功能;基于注解的 TestContext 測試框架。

Spring 推薦開發者使用新的基于注解的 TestContext 測試框架,本文我們將對此進行詳細的講述。

低版本的 Spring 所提供的 Spring 測試框架構在 JUnit 3.8 基礎上擴展而來,它提供了若干個測試基類。而 Spring 2.5 所新增的基于注解的 TestContext 測試框架和低版本的測試框架沒有任何關系。它采用全新的注解技術可以讓 POJO 成為 Spring 的測試用例,除了擁有舊測試框架所有功能外,TestContext 還添加了一些新的功能,TestContext 可以運行在 JUnit 3.8、JUnit 4.4、TestNG 等測試框架下。

回頁首

直接使用 JUnit 測試 Spring 程序存在的不足

在拙作《精通 Spring 2.x — 企業應用開發詳解》一書中,筆者曾經指出如果直接使用 JUnit 測試基于 Spring 的程序,將存在以下 4 點明顯的不足:

導致 Spring 容器多次初始化問題:根據 JUnit 測試用例的調用流程,每執行一個測試方法都會重新創建一個測試用例實例并調用其 setUp() 方法。由于在一般情況下,我們都在 setUp() 方法中初始化 Spring 容器,這意味著測試用例中有多少個測試方法,Spring 容器就會被重復初始化多少次。需要使用硬編碼方式手工獲取 Bean:在測試用例中,我們需要通過 applicationContext.getBean() 的方法從 Spirng 容器中獲取需要測試的目標 Bean,并且還要進行造型操作。數據庫現場容易遭受破壞:測試方法可能會對數據庫記錄進行更改操作,破壞數據庫現場。雖然是針對開發數據庫進行測試工作的,但如果數據操作的影響是持久的,將會形成積累效應并影響到測試用例的再次執行。舉個例子,假設在某個測試方法中往數據庫插入一條 ID 為 1 的 t_user 記錄,第一次運行不會有問題,第二次運行時,就會因為主鍵沖突而導致測試用例執行失敗。所以測試用例應該既能夠完成測試固件業務功能正確性的檢查,又能夠容易地在測試完成后恢復現場,做到踏雪無跡、雁過無痕。不容易在同一事務下訪問數據庫以檢驗業務操作的正確性:當測試固件操作數據庫時,為了檢測數據操作的正確性,需要通過一種方便途徑在測試方法相同的事務環境下訪問數據庫,以檢查測試固件數據操作的執行效果。如果直接使用 JUnit 進行測試,我們很難完成這項操作。

Spring 測試框架是專門為測試基于 Spring 框架應用程序而設計的,它能夠讓測試用例非常方便地和 Spring 框架結合起來,以上所有問題都將迎刃而解。

一個需要測試的 Spring 服務類

在具體使用 TextContext 測試框架之前,我們先來認識一下需要測試的 UserService 服務類。UserService 服務類中擁有一個處理用戶登錄的服務方法,其代碼如下所示:

清單1. UserService.java 需要測試的服務類

[java] view plain copy print?在CODE上查看代碼片package com.baobaotao.service;    import com.baobaotao.domain.LoginLog;  import com.baobaotao.domain.User;  import com.baobaotao.dao.UserDao;  import com.baobaotao.dao.LoginLogDao;    public class UserService{        private UserDao userDao;      private LoginLogDao loginLogDao;        public void handleUserLogin(User user) {          user.setCredits( 5 + user.getCredits());          LoginLog loginLog = new LoginLog();          loginLog.setUserId(user.getUserId());          loginLog.set<?xml version="1.0" encoding="UTF-8" ?>  <beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xmlns:p="http://www.springframework.org/schema/p"      xmlns:tx="http://www.springframework.org/schema/tx"  xmlns:aop="http://www.springframework.org/schema/aop"  xmlns:context="http://www.springframework.org/schema/context"  xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd       http://www.springframework.org/schema/tx   http://www.springframework.org/schema/tx/spring-tx-2.5.xsd       http://www.springframework.org/schema/aop   http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">      <!-- 配置數據源  -->  <bean id="dataSource"  class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close"          p:driverClassName="com.MySQL.jdbc.Driver"          p:url="jdbc:mysql://localhost/sampledb"          p:username="root"          p:passWord="1234"/>            <!-- 配置Jdbcpackage com.baobaotao.service;    import org.springframework.test.context.junit4.      AbstractTransactionalJUnit4SpringContextTests;  import org.springframework.test.context.ContextConfiguration;  import org.springframework.beans.factory.annotation.Autowired;  import org.junit.Test;  import com.baobaotao.domain.User;    import java.util.Date;    @ContextConfiguration  //①  public class TestUserService extends       AbstractTransactionalJUnit4SpringContextTests {       @Autowired  //②     private UserService userService;       @Test  //③     public void handleUserLogin(){         User user = new User();         user.setUserId(1);         user.setLastIp("127.0.0.1");         Date now = new Date();         user.setLastVisit(now.getTime());         userService.handleUserLogin(user);     }  }  這里,我們讓%20TestUserService%20直接繼承于%20Spring%20所提供的%20AbstractTransactionalJUnit4SpringContextTests%20的抽象測試類,稍后本文將對這個抽象測試類進行剖析,這里你僅須知道該抽象測試類的作用是讓%20TestContext%20測試框架可以在%20JUnit%204.4%20測試框架基礎上運行起來就可以了。

在%20①%20處,標注了一個類級的%20@ContextConfiguration%20注解,這里%20Spring%20將按%20TestContext%20契約查找%20classpath:/com/baobaotao/service/TestUserService-context.xml%20的%20Spring%20配置文件,并使用該配置文件啟動%20Spring%20容器。@ContextConfiguration%20注解有以下兩個常用的屬性:

locations:可以通過該屬性手工指定%20Spring%20配置文件所在的位置,可以指定一個或多個%20Spring%20配置文件。如下所示:

@ContextConfiguration(locations={“xx/yy/beans1.xml”,”%20xx/yy/beans2.xml”})

inheritLocations:是否要繼承父測試用例類中的%20Spring%20配置文件,默認為%20true。如下面的例子:

[java] view%20plain copy print?@ContextConfiguration(locations={"base-context.xml"})   public class BaseTest {       // ...   }   @ContextConfiguration(locations={"extended-context.xml"})   public class ExtendedTest extends BaseTest {       // ...   }  如果%20inheritLocations%20設置為%20false,則%20ExtendedTest%20僅會使用%20extended-context.xml%20配置文件,否則將使用%20base-context.xml%20和%20extended-context.xml%20這兩個配置文件。

②%20處的%20@Autowired%20注解讓%20Spring%20容器自動注入%20UserService%20類型的%20Bean。而在%20③%20處標注的%20@Test%20注解則讓%20handleUserLogin()%20方法成為一個%20JUnit%204.4%20標準的測試方法,%20@Test%20是%20JUnit%204.4%20所定義的注解。

在運行%20TestUserService%20測試類之前,讓我們先看一下%20TestUserService-context.xml%20配置文件的內容:

清單%204.TestUserService%20所引用的%20Spring%20配置文件

[xml] view%20plain copy print?<?xml version="1.0" encoding="UTF-8" ?>  <beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">    <!-- ① 引入清單1定義的Spring配置文件 -->  <import resource="classpath:/applicationContext.xml"/>    </beans>  在%20①%20處引入了清單%201%20中定義的%20Spring%20配置文件,這樣我們就可以將其中定義的%20UserService%20Bean%20作為測試固件注入到%20TestUserService%20中了。

在你的%20IDE%20中(Eclipse、JBuilder、Idea%20等),將%20JUnit%204.4%20類包引入到項目工程中后,在%20TestUserService%20類中點擊右鍵運行該測試類,將發現%20TestUserService%20已經可以成功運行了,如%20圖%201%20所示:

圖%201.%20在%20Eclipse%206.0%20中運行%20TestUserService

TestUserService 可以正確運行,說明其 userService 這個測試固件已經享受了 Spring 自動注入的功能。在運行該測試用例后,到數據庫中查看 t_user 表和 t_login_log 表,你會發現表數據和測試前是一樣的!這說明雖然我們在清單 3 的 handleUserLogin() 測試方法中執行了 userService.handleUserLogin(user) 的操作,但它并沒有對數據庫現場造成破壞:這是因為 Spring 的在測試方法返回前進行了事務回滾操作。

雖然 TestUserService.handleUserLogin() 測試方法已經可以成功運行,但是它在測試功能上是不完善的,讀者朋友可以已經發現了它存在以下兩個問題:

我們僅僅執行了 UserService#handleUserLogin(user) 方法,但驗證該方法執行結果的正確性。在測試方法中直接使用 ID 為 1 的 User 對象進行測試,這相當于要求在數據庫 t_user 表必須已經存在 ID 為 1 的記錄,如果 t_user 中不存在這條記錄,將導致測試方法執行失敗。

回頁首

準備測試數據并檢測運行結果

在這節里,我們將著手解決上面所提出的兩個問題,在測試用例中準備測試數據并到數據庫中檢測業務執行結果的正確性。

準備測試數據

相比于在測試方法中直接訪問預定的數據記錄,在測試方法執行前通過程序準備一些測試數據,然后在此基礎上運行測試方法是比較好的策略,因為后者不需要對數據庫的狀態做假設。在 TestContext 中,你可以通過使用 JUnit 4.4 的 @Before 注解達到這個目的,請看下面的代碼:

清單5. 為測試方法準備數據

[java] view plain copy print?在CODE上查看代碼片package com.baobaotao.service;    import java.sql.Connection;  import java.sql.PreparedStatement;  import java.sql.SQLException;  import java.util.Date;    import org.junit.Before;  import org.junit.Test;  import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.jdbc.core.PreparedStatementCreator;  import org.springframework.jdbc.support.GeneratedKeyHolder;  import org.springframework.jdbc.support.KeyHolder;  import org.springframework.test.context.ContextConfiguration;  import org.springframework.test.context.junit4.  AbstractTransactionalJUnit4SpringContextTests;    import com.baobaotao.dao.UserDao;  import com.baobaotao.domain.User;    @ContextConfiguration  public class TestUserService   extends AbstractTransactionalJUnit4SpringContextTests {      @Autowired      private UserService userService;           @Autowired      private UserDao userDao;           private int userId;           @Before //① 準備測試數據      public void prepareTestData() {          final String  sql = "insert into t_user(user_name,password) values('tom','1234')";          simpleJdbcTemplate.update(sql);          KeyHolder keyHolder = new GeneratedKeyHolder();          simpleJdbcTemplate.getJdbcOperations().update(              new PreparedStatementCreator() {                  public PreparedStatement createPreparedStatement(Connection conn)                      throws SQLException {                      PreparedStatement ps = conn.prepareStatement(sql);                      return ps;                  }              }, keyHolder);          userId = keyHolder.getKey().intValue();//①-1 記錄測試數據的id      }           @Test      public void handleUserLogin(){          User user = userDao.getUserById(userId); //② 獲取測試數據          user.setLastIp("127.0.0.1");          Date now = new Date();          user.setLastVisit(now.getTime());          userService.handleUserLogin(user);      }  }  JUnit%204.4%20允許通過注解指定某些方法在測試方法執行前后進行調用,即是%20@Before%20和%20@After%20注解。在%20Spring%20TestContext%20中,標注%20@Before%20和%20@After%20的方法會在測試用例中每個測試方法運行前后執行,并和測試方法運行于同一個事務中。在%20清單%205%20中%20①%20處,我們給%20prepareTestData()%20標注上了%20@Before%20注解,在該方法中準備一些測試數據,以供%20TestUserService%20中所有測試方法使用(這里僅有一個%20handleUserLogin()%20測試方法)。由于測試方法運行后,整個事務會被回滾,在%20prepareTestData()%20中插入的測試數據也不會持久化到數據庫中,因此我們無須手工刪除這條記錄。

標注%20@Before%20或%20@After%20注解的方法和測試方法運行在同一個事務中,但有時我們希望在測試方法的事務開始之前或完成之后執行某些方法以便獲取數據庫現場的一些情況。這時,可以使用%20Spring%20TestContext%20的%20@BeforeTransaction%20和%20@AfterTransaction%20注解來達到目錄(這兩個注解位于%20org.springframework.test.context.transaction%20包中)。

雖然大多數業務方法都會訪問數據庫,但也并非所有需要測試的業務方法都需要和數據庫打交道。而在默認情況下,繼承于%20AbstractTransactionalJUnit4SpringContextTests%20測試用例的所有測試方法都將工作于事務環境下,你可以顯式地通過%20@NotTransactional%20注解,讓測試方法不工作于事務環境下。

prepareTestData()%20方法中使用到了%20simpleJdbcTemplate%20對象訪問操作數據庫,該對象在%20AbstractTransactionalJUnit4SpringContextTests%20抽象類中定義,只要%20Spring%20容器有配置數據源,simpleJdbcTemplate%20就會被自動創建。同時該抽象類中還擁有一個%20Spring%20容器引用:applicationContext,你可以借助該成員變量訪問%20Spring%20容器,執行獲取%20Bean,發布事件等操作。

此外,AbstractTransactionalJUnit4SpringContextTests%20還提供了若干個訪問數據庫的便捷方法,說明如下:

protected%20int%20countRowsInTable(String%20tableName)%20:計算數據表的記錄數。protected%20int%20deleteFromTables(String...%20names):刪除表中的記錄,可以指定多張表。protected%20void%20executeSqlScript(String%20sqlResourcePath,%20boolean%20continueOnError):執行%20SQL%20腳本文件,在腳本文件中,其格式必須一個%20SQL%20語句一行。在測試方法%20handleUserLogin()%20的%20②%20處,我們通過%20userDao%20獲取%20prepareTestData()%20添加的測試數據,測試方法在測試數據的基礎上執行業務邏輯。使用這種測試方式后,在任何情況下運行%20TestUserService%20都不會發生業務邏輯之外的問題。

檢驗業務邏輯的正確性到目前為此,TestUserService%20的%20handleUserLogin()%20測試方法僅是簡單地執行%20UserService#handleUserLogin()%20業務方法,但并沒有在業務方法執行后檢查執行結果的正確性,因此這個測試是不到位的。也就是說,我們必須訪問數據庫以檢查業務方法對數據更改是否成功:這包括積分(credits)、最后登錄時間(last_visit)、最后登錄%20IP(last_ip)以及登錄日志表中的登錄日志記錄(t_login_log)。下面,我們補充這項重要的檢查數據正確性的工作:

清單5.%20檢驗業務方法執行結果的正確性

[java] view%20plain copy print?@Test  public void handleUserLogin(){      User user = userDao.getUserById(userId);      user.setLastIp("127.0.0.1");      Date now = new Date();      user.setLastVisit(now.getTime());      userService.handleUserLogin(user);        //------------------以下為業務執行結果檢查的代碼---------------------      User newUser = userDao.getUserById(userId);      Assert.assertEquals(5, newUser.getCredits()); //①檢測積分      //①檢測最后登錄時間和IP      Assert.assertEquals(now.getTime(), newUser.getLastVisit());      Assert.assertEquals("127.0.0.1",newUser.getLastIp());               // ③檢測登錄記錄      String sql = "select count(1) from t_login_log where user_id=? "+          “ and login_datetime=? and ip=?";      int logCount =simpleJdbcTemplate.queryForInt(sql, user.getUserId(),          user.getLastVisit(),user.getLastIp());      Assert.assertEquals(1, logCount);      }  在業務方法執行后,我們查詢數據庫中相應記錄以檢查是否和期望的效果一致,如%20①%20和%20②%20所示。在%20③%20處,我們使用%20SimpleJdbcTemplate%20查詢%20t_login_log,以檢查該表中是否已經添加了一條用戶登錄日志。

注意:由于我們的%20DAO%20層采用%20Spring%20JDBC%20框架,它沒有采用服務層緩存技術,所以可以使用%20DAO%20類返回數據庫中的數據。如果采用hibernate 等%20ORM%20框架,由于它們采用了服務層緩存的技術,為了獲取數據庫中的相應數據,需要在業務方法執行后調用%20HibernateTemplate.flush()%20方法,將緩存中的對象同步到數據庫中,這時才可以通過%20SimpleJdbcTemplate%20在數據庫中訪問業務方法的執行情況。

回頁首

Spring%20TestContext%20測試框架體系結構在前面,我們直接通過擴展%20AbstractTransactionalJUnit4SpringContextTests%20編寫測試用例,在了解了編寫基于%20TestContext%20測試框架的測試用例后,現在是了解%20TestContext%20測試框架本身的時候了。

TestContext%20核心類、支持類以及注解類TestContext%20測試框架的核心由%20org.springframework.test.context%20包中三個類組成,分別是%20TestContext%20和%20TestContextManager%20類以及%20TestExecutionListener%20接口。其類圖如下%20圖%202%20所示:

圖%202.%20Spring%20TestContext%20測試框架核心類

TestContext:它封裝了運行測試用例的上下文;TestContextManager:它是進入 Spring TestContext 框架的程序主入口,它管理著一個 TestContext 實例,并在適合的執行點上向所有注冊在 TestContextManager 中的 TestExecutionListener 監聽器發布事件:比如測試用例實例的準備,測試方法執行前后方法的調用等。TestExecutionListener:該接口負責響應 TestContextManager 發布的事件。

Spring TestContext 允許在測試用例類中通過 @TestExecutionListeners 注解向 TestContextManager 注冊多個監聽器,如下所示:

[java] view plain copy print?在CODE上查看代碼片@TestExecutionListeners( {       DependencyInjectionTestExecutionListener.class,      DirtiesContextTestExecutionListener.class })  public class TestXxxService{      …  }  Spring%20提供了幾個%20TestExecutionListener%20接口實現類,分別說明如下:

DependencyInjectionTestExecutionListener:該監聽器提供了自動注入的功能,它負責解析測試用例中%20@Autowried%20注解并完成自動注入;DirtiesContextTestExecutionListener:一般情況下測試方法并不會對%20Spring%20容器上下文造成破壞(改變%20Bean%20的配置信息等),如果某個測試方法確實會破壞%20Spring%20容器上下文,你可以顯式地為該測試方法添加%20@DirtiesContext%20注解,以便%20Spring%20TestContext%20在測試該方法后刷新%20Spring%20容器的上下文,而%20DirtiesContextTestExecutionListener%20監聽器的工作就是解析%20@DirtiesContext%20注解;TransactionalTestExecutionListener:它負責解析%20@Transaction、@NotTransactional%20以及%20@Rollback%20等事務注解的注解。@Transaction%20注解讓測試方法工作于事務環境中,不過在測試方法返回前事務會被回滾。你可以使用%20@Rollback(false)%20讓測試方法返回前提交事務。而%20@NotTransactional%20注解則讓測試方法不工作于事務環境中。此外,你還可以使用類或方法級別的%20@TransactionConfiguration%20注解改變事務管理策略,如下所示:[java] view%20plain copy print?@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)  @Transactional  public class TestUserService {      …  }  我們知道在%20JUnit%204.4%20中可以通過%20@RunWith%20注解指定測試用例的運行器,Spring%20TestContext%20框架提供了擴展于%20org.junit.internal.runners.JUnit4ClassRunner%20的%20SpringJUnit4ClassRunner%20運行器,它負責總裝%20Spring%20TestContext%20測試框架并將其統一到%20JUnit%204.4%20框架中。

TestContext%20所提供的抽象測試用例Spring%20TestContext%20為基于%20JUnit%204.4%20測試框架提供了兩個抽象測試用例類,分別是%20AbstractJUnit4SpringContextTests%20和%20AbstractTransactionalJUnit4SpringContextTests,而后者擴展于前者。讓我們來看一下這兩個抽象測試用例類的骨架代碼:

[java] view%20plain copy print?@RunWith(SpringJUnit4ClassRunner.class) //① 指定測試用例運行器  @TestExecutionListeners(                 //② 注冊了兩個TestExecutionListener監聽器      { DependencyInjectionTestExecutionListener.class,      DirtiesContextTestExecutionListener.class })  public class AbstractJUnit4SpringContextTests implements ApplicationContextAware {      …  }  ①%20處將%20SpringJUnit4ClassRunner%20指定為測試用例運行器,它負責無縫地將%20TestContext%20測試框架移花接木到%20JUnit%204.4%20測試框架中,它是%20Spring%20TestContext%20可以運行起來的根本所在。②%20處通過%20@TestExecutionListeners%20注解向測試用例類中注冊了兩個%20TestExecutionListener%20監聽器,這兩個監聽器分別負責對%20@Autowired%20和%20@DirtiesContext%20注解進行處理,為測試用例提供自動注入和重新刷新%20Spring%20容器上下文的功能。

AbstractTransactionalJUnit4SpringContextTests%20擴展于%20AbstractJUnit4SpringContextTests,提供了事務管理的支持,其骨架代碼如下所示:

[java] view%20plain copy print?派生到我的代碼片//① 注冊測試用例事務管理的監聽器  @TestExecutionListeners( { TransactionalTestExecutionListener.class })  @Transactional    //② 使測試用例的所有方法都將工作于事務環境下  public class AbstractTransactionalJUnit4SpringContextTests   extends AbstractJUnit4SpringContextTests {      …  }  

在 ① 處,AbstractTransactionalJUnit4SpringContextTests 向測試用例類中注冊了 TransactionalTestExecutionListener 監聽器,這樣測試用例中的 @Transaction、@NotTransaction 以及 @Rollback 等注解就可以正確地工作起來了。注意,你不需要在 Spring 配置文件通過 <tx:annotation-driven /> 和 <context:annotation-config/> 為測試用例類啟用注解事務驅動和注解自動注入,這個工作完全于 TestContext 自身來解決(通過注冊 DependencyInjectionTestExecutionListener 和 TransactionalTestExecutionListener 監聽器),畢竟測試用例類沒有注冊到 Spring 容器中,沒有成為 Spring 的 Bean。

小結

我們通過對一個典型的涉及數據庫訪問操作的 UserService 服務類的測試,講述了使用 Spring 2.5 TestContext 測試框架進行集成測試的各項問題,這包括測試固件的自動注入、事務自動回滾、通過 SimpleJdbcTemplate 直接訪問數據庫以及測試數據準備等問題。

在通過一個實際例子的學習后,我們對如何使用 TestContext 測試框架有了一個具體的認識,在此基礎上我們對 Spring TestContext 測試框架體系結構進行了分析,然后剖析了 Spring 為 TestContext 嫁接到 JUnit 4.4 測試框架上所提供的兩個抽象測試用例類。

Spring 的 TestContext 測試框架不但可以整合到 JUnit 4.4 測試框架上,而且還可以整合到 JUnit 3.8 以及 TestNG 等測試框架上。目前已經提供了對 JUnit 3.8 以及 TestNG 的支持,你可以分別在 org.springframework.test.context.junit38 和 org.springframework.test.context.testng 包下找到整合的幫助類。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 海丰县| 当雄县| 云浮市| 北碚区| 阳西县| 永靖县| 遵义县| 涞源县| 大英县| 繁昌县| 库尔勒市| 五峰| 祁阳县| 伊金霍洛旗| 三江| 外汇| 阿勒泰市| 桐柏县| 镇沅| 遂川县| 定边县| 抚州市| 府谷县| 丰原市| 连云港市| 甘洛县| 商洛市| 四平市| 将乐县| 特克斯县| 岗巴县| 定结县| 稻城县| 仙桃市| 普陀区| 大英县| 洛扎县| 噶尔县| 丹寨县| 永丰县| 昌邑市|