上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0
          初次用文字的方式記錄讀源碼的過程,不知道怎么寫,感覺有點(diǎn)貼代碼的嫌疑。不過中間還是加入了一些自己的理解和心得,希望以后能夠慢慢的改進(jìn),感興趣的童鞋湊合著看吧,感覺JUnit這個(gè)框架還是值得看的,里面有許多不錯(cuò)的設(shè)計(jì)思想在,更何況它是Kent Beck和Erich Gamma這樣的大師寫的。。。。。

          深入JUnit源碼之Rule

          JUnit中的Rule是對(duì)@BeforeClass@AfterClass@Before@After等注解的另一種實(shí)現(xiàn),其中@ClassRule實(shí)現(xiàn)的功能和@BeforeClass@AfterClass類似;@Rule實(shí)現(xiàn)的功能和@Before@after類似。JUnit引入@ClassRule@Rule注解的關(guān)鍵是想讓以前在@BeforeClass@AfterClass@Before@After中的邏輯能更加方便的實(shí)現(xiàn)重用,因?yàn)?/span>@BeforeClass@AfterClass@Before@After是將邏輯封裝在一個(gè)測(cè)試類的方法中的,如果實(shí)現(xiàn)重用,需要自己將這些邏輯提取到一個(gè)單獨(dú)的類中,再在這些方法中調(diào)用,而@ClassRule@Rule則是將邏輯封裝在一個(gè)類中,當(dāng)需要使用時(shí),直接賦值即可,對(duì)不需要重用的邏輯則可用匿名類實(shí)現(xiàn),也因此,JUnit在接下來的版本中更傾向于多用@ClassRule@Rule,雖然就我自己來說,感覺還是用@BeforeClass@AfterClass@Before@After這些注解更加熟悉一些,也可能是我測(cè)試代碼寫的還不夠多的原因吧L。同時(shí)由于Statement鏈構(gòu)造的特殊性@ClassRule@Rule也保證了類似父類@BeforeClass@Before注解的方法要比子類的注解方法執(zhí)行早,而父類的@AfterClass@After注解的方法執(zhí)行要比子類要早的特點(diǎn)。

          @ClassRule@Rule注解字段的驗(yàn)證

          @ClassRule@Rule只能注解在字段中,并且該字段的類型必須實(shí)現(xiàn)了TestRule接口,對(duì)@ClassRule注解的字段還必須是publicstatic,并且@ClassRule注解的字段在運(yùn)行時(shí)不可以拋異常,不然JUnit的行為是未定義的,這個(gè)是注釋文檔中這樣描述的,實(shí)際情況則一般是直接觸發(fā)testFailure事件,至于其他結(jié)果,則要看不同的TestRule實(shí)現(xiàn)不同,這個(gè)特征將在下面詳細(xì)講解;而對(duì)@Rule注解的字段必須是public,非static,關(guān)于@ClassRule注解字段和@Rule注解字段的驗(yàn)證是在RuleFieldValidator中做的(具體可以參考Runner小節(jié))

           1 public enum RuleFieldValidator {
           2     CLASS_RULE_VALIDATOR(ClassRule.classtrue), RULE_VALIDATOR(Rule.classfalse);
           3     
           4     public void validate(TestClass target, List<Throwable> errors) {
           5        List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation);
           6        for (FrameworkField each : fields)
           7            validateField(each, errors);
           8     }
           9     private void validateField(FrameworkField field, List<Throwable> errors) {
          10        optionallyValidateStatic(field, errors);
          11        validatePublic(field, errors);
          12        validateTestRuleOrMethodRule(field, errors);
          13     }
          14     private void optionallyValidateStatic(FrameworkField field,
          15            List<Throwable> errors) {
          16        if (fOnlyStaticFields && !field.isStatic())
          17            addError(errors, field, "must be static.");
          18     }
          19     private void validatePublic(FrameworkField field, List<Throwable> errors) {
          20        if (!field.isPublic())
          21            addError(errors, field, "must be public.");
          22     }
          23     private void validateTestRuleOrMethodRule(FrameworkField field,
          24            List<Throwable> errors) {
          25        if (!isMethodRule(field) && !isTestRule(field))
          26            addError(errors, field, "must implement MethodRule or TestRule.");
          27     }
          28     private boolean isTestRule(FrameworkField target) {
          29        return TestRule.class.isAssignableFrom(target.getType());
          30     }
          31     private boolean isMethodRule(FrameworkField target) {
          32        return org.junit.rules.MethodRule.class.isAssignableFrom(target
          33               .getType());
          34     }
          35     private void addError(List<Throwable> errors, FrameworkField field,
          36            String suffix) {
          37        String message= "The @" + fAnnotation.getSimpleName() + " '"
          38               + field.getName() + "" + suffix;
          39        errors.add(new Exception(message));
          40     }
          41 }

           

          JUnit默認(rèn)實(shí)現(xiàn)的TestRule

          本節(jié)將重點(diǎn)介紹當(dāng)前JUnit默認(rèn)實(shí)現(xiàn)的幾個(gè)TestRule,先給出類圖,然后介紹源碼實(shí)現(xiàn)以及用途,最后還將簡(jiǎn)單的介紹RunRules這個(gè)Statement的運(yùn)行信息,雖然這個(gè)類非常簡(jiǎn)單,在Statement那節(jié)中也已經(jīng)簡(jiǎn)單的做過介紹了。

          在學(xué)一個(gè)新的框架的時(shí)候,我一直比較喜歡先看一下框架的類圖,這樣自己總體上就有個(gè)概念了。這里也先給一張JUnitTestRule的類圖吧:

           

          TestRule的類結(jié)構(gòu)圖還是比較簡(jiǎn)單的,只是將它置于JUnitStatement框架中,有些問題分析起來就比較復(fù)雜了。為了保持問題的簡(jiǎn)單,我們先來看一下每個(gè)單獨(dú)的類各自實(shí)現(xiàn)了什么功能和怎么實(shí)現(xiàn)吧。

          TestWatcherTestName

          先來看兩個(gè)簡(jiǎn)單的吧,TestWatcher為子類提供了四個(gè)事件方法以監(jiān)控測(cè)試方法在運(yùn)行過程中的狀態(tài),一般它可以作為信息記錄使用。如果TestWatcher作為@ClassRule注解字段,則該測(cè)試類在運(yùn)行之前(調(diào)用所有的@BeforeClass注解方法之前)會(huì)調(diào)用starting()方法;當(dāng)所有@AfterClass注解方法調(diào)用結(jié)束后,succeeded()方法會(huì)被調(diào)用;若@AfterClass注解方法中出現(xiàn)異常,則failed()方法會(huì)被調(diào)用;最后,finished()方法會(huì)被調(diào)用;所有這些方法的DescriptionRunner對(duì)應(yīng)的Description。如果TestWatcher作為@Rule注解字段,則在每個(gè)測(cè)試方法運(yùn)行前(所有的@Before注解方法運(yùn)行前)會(huì)調(diào)用starting()方法;當(dāng)所有@After注解方法調(diào)用結(jié)束后,succeeded()方法會(huì)被調(diào)用;若@After注解方法中跑出異常,則failed()方法會(huì)被調(diào)用;最后,finished()方法會(huì)被調(diào)用;所有Description的實(shí)例是測(cè)試方法的Description實(shí)例。

          TestName是對(duì)TestWatcher的一個(gè)簡(jiǎn)單實(shí)現(xiàn),它會(huì)在starting()方法中記錄每次運(yùn)行的名字。如果TestName作為@Rule注解字段,則starting()中傳入的Description是對(duì)每個(gè)測(cè)試方法的Description,因而getMethodName()方法返回的是測(cè)試方法的名字。一般TestName不作為@ClassRule注解字段,如果真有人這樣用了,則starting()Description的參數(shù)是RunnerDescription實(shí)例,一般getMethodName()返回值為null

           1 public abstract class TestWatcher implements TestRule {
           2     public Statement apply(final Statement base, final Description description) {
           3        return new Statement() {
           4            @Override
           5            public void evaluate() throws Throwable {
           6               starting(description);
           7               try {
           8                   base.evaluate();
           9                   succeeded(description);
          10               } catch (AssumptionViolatedException e) {
          11                   throw e;
          12               } catch (Throwable t) {
          13                   failed(t, description);
          14                   throw t;
          15               } finally {
          16                   finished(description);
          17               }
          18            }
          19        };
          20     }
          21     protected void succeeded(Description description) {
          22     }
          23     protected void failed(Throwable e, Description description) {
          24     }
          25     protected void starting(Description description) {
          26     }
          27     protected void finished(Description description) {
          28     }
          29 }
          30 public class TestName extends TestWatcher {
          31     private String fName;
          32     @Override
          33     protected void starting(Description d) {
          34        fName= d.getMethodName();
          35     }
          36     public String getMethodName() {
          37        return fName;
          38     }
          39 }

           

          ExternalResourceTemporaryFolder

          ExternalResource為子類提供了兩個(gè)接口,分別是進(jìn)入測(cè)試之前和退出測(cè)試之后,一般它是作為對(duì)一些資源在測(cè)試前后的控制,如Socket的開啟與關(guān)閉、Connection的開始與斷開、臨時(shí)文件的創(chuàng)建與刪除等。如果ExternalResource用在@ClassRule注解字段中,before()方法會(huì)在所有@BeforeClass注解方法之前調(diào)用;after()方法會(huì)在所有@AfterClass注解方法之后調(diào)用,不管在執(zhí)行@AfterClass注解方法時(shí)是否拋出異常。如果ExternalResource用在@Rule注解字段中,before()方法會(huì)在所有@Before注解方法之前調(diào)用;after()方法會(huì)在所有@After注解方法之后調(diào)用。

          TemporaryFolder是對(duì)ExternalResource的一個(gè)實(shí)現(xiàn),它在before()方法中在臨時(shí)文件夾中創(chuàng)建一個(gè)隨機(jī)的文件夾,以junit開頭;并在after()方法將創(chuàng)建的臨時(shí)文件夾清空,并刪除該臨時(shí)文件夾。另外TemporaryFolder還提供了幾個(gè)方法以在新創(chuàng)建的臨時(shí)文件夾中創(chuàng)建新的文件、文件夾。

           1 public abstract class ExternalResource implements TestRule {
           2     public Statement apply(Statement base, Description description) {
           3        return statement(base);
           4     }
           5     private Statement statement(final Statement base) {
           6        return new Statement() {
           7            @Override
           8            public void evaluate() throws Throwable {
           9               before();
          10               try {
          11                   base.evaluate();
          12               } finally {
          13                   after();
          14               }
          15            }
          16        };
          17     }
          18     protected void before() throws Throwable {
          19     }
          20     protected void after() {
          21     }
          22 }
          23 public class TemporaryFolder extends ExternalResource {
          24     private File folder;
          25     @Override
          26     protected void before() throws Throwable {
          27        create();
          28     }
          29     @Override
          30     protected void after() {
          31        delete();
          32     }
          33     public void create() throws IOException {
          34        folder= newFolder();
          35     }
          36     public File newFile(String fileName) throws IOException {
          37        File file= new File(getRoot(), fileName);
          38        file.createNewFile();
          39        return file;
          40     }
          41     public File newFile() throws IOException {
          42        return File.createTempFile("junit"null, folder);
          43     }
          44     public File newFolder(String folderNames) {
          45        File file = getRoot();
          46        for (String folderName : folderNames) {
          47            file = new File(file, folderName);
          48            file.mkdir();
          49        }
          50        return file;
          51     }
          52     public File newFolder() throws IOException {
          53        File createdFolder= File.createTempFile("junit""", folder);
          54        createdFolder.delete();
          55        createdFolder.mkdir();
          56        return createdFolder;
          57     }
          58     public File getRoot() {
          59        if (folder == null) {
          60            throw new IllegalStateException("the temporary folder has not yet been created");
          61        }
          62        return folder;
          63     }
          64     public void delete() {
          65        recursiveDelete(folder);
          66     }
          67     private void recursiveDelete(File file) {
          68        File[] files= file.listFiles();
          69        if (files != null)
          70            for (File each : files)
          71               recursiveDelete(each);
          72        file.delete();
          73     }
          74 }

           

          VerifierErrorCollector

          Verifier是在所有測(cè)試已經(jīng)結(jié)束的時(shí)候,再加入一些額外的邏輯,如果額外的邏輯通過,才表示測(cè)試成功,否則,測(cè)試依舊失敗,即使在之前的運(yùn)行中都是成功的。Verify可以為一些很多測(cè)試方法加入一些公共的驗(yàn)證邏輯。當(dāng)Verifier應(yīng)用在@Rule注解字段中,它在所偶@After注解方法運(yùn)行完后,會(huì)調(diào)用verify()方法,如果verifier()方法驗(yàn)證失敗拋出異常,則該測(cè)試方法的testFailure事件將會(huì)被觸發(fā),導(dǎo)致該測(cè)試方法失敗;當(dāng)Verifier應(yīng)用在@ClassRule時(shí),它在所有的@AfterClass注解的方法執(zhí)行完后,會(huì)執(zhí)行verify()方法,如果verify失敗拋出異常,將會(huì)觸發(fā)關(guān)于該測(cè)試類的testFailure,此時(shí)測(cè)試類中的所有測(cè)試方法都已經(jīng)運(yùn)行成功了,卻在最后收到一個(gè)關(guān)于測(cè)試類的testFailure事件,這確實(shí)是一個(gè)比較詭異的事情,因而@ClassRule中提到ErrorCollectorVerifier)不可以用在@ClassRule注解中,否則其行為為定義;更一般的@ClassRule注解的字段運(yùn)行時(shí)不能拋異常,不然其行為是未定義的。

          ErrorCollector是對(duì)Verifier的一個(gè)實(shí)現(xiàn),它可以在運(yùn)行測(cè)試方法的過程中收集錯(cuò)誤信息,而這些錯(cuò)誤信息知道最后調(diào)用ErrorCollectorverify()方法時(shí)再處理。其實(shí)就目前來看,我很難想象這個(gè)需求存在的意義,因?yàn)榧词顾鼘⑺械腻e(cuò)誤信息收集在一起了,在事件發(fā)布是,它還是會(huì)為每個(gè)錯(cuò)誤發(fā)布一次testFailure事件(參考EachTestNotifier的實(shí)現(xiàn)),除非有一種需求是即使測(cè)試方法在運(yùn)行過程的某個(gè)點(diǎn)運(yùn)行出錯(cuò),也只是先記錄這個(gè)錯(cuò)誤,等到所有邏輯運(yùn)行結(jié)束后才去將這個(gè)測(cè)試方法運(yùn)行過程中存在的錯(cuò)誤發(fā)布出去,這樣一次運(yùn)行就可以知道測(cè)試代碼中存在出錯(cuò)的地方。ErrorCollector中還提供了幾個(gè)收集錯(cuò)誤的方法:如addError()checkThat()checkSucceeds()等。這里的checkThat()方法用到了hamcrest框架中的Matcher,這部分的內(nèi)容將在Assert小節(jié)中詳細(xì)介紹。

           1 public class Verifier implements TestRule {
           2     public Statement apply(final Statement base, Description description) {
           3        return new Statement() {
           4            @Override
           5            public void evaluate() throws Throwable {
           6               base.evaluate();
           7               verify();
           8            }
           9        };
          10     }
          11     protected void verify() throws Throwable {
          12     }
          13 }
          14 public class ErrorCollector extends Verifier {
          15     private List<Throwable> errors= new ArrayList<Throwable>();
          16     @Override
          17     protected void verify() throws Throwable {
          18        MultipleFailureException.assertEmpty(errors);
          19     }
          20     public void addError(Throwable error) {
          21        errors.add(error);
          22     }
          23     public <T> void checkThat(final T value, final Matcher<T> matcher) {
          24        checkThat("", value, matcher);
          25     }
          26     public <T> void checkThat(final String reason, final T value, final Matcher<T> matcher) {
          27        checkSucceeds(new Callable<Object>() {
          28            public Object call() throws Exception {
          29               assertThat(reason, value, matcher);
          30               return value;
          31            }
          32        });
          33     }
          34     public Object checkSucceeds(Callable<Object> callable) {
          35        try {
          36            return callable.call();
          37        } catch (Throwable e) {
          38            addError(e);
          39            return null;
          40        }
          41     }
          42 }

           

          TimeoutExpectedException

          TimeoutExpectedException都是對(duì)@Test注解中timeoutexpected字段的部分替代實(shí)現(xiàn)。而且不同于@Test中的注解只適用于單個(gè)測(cè)試方法,這兩個(gè)實(shí)現(xiàn)適用于全局測(cè)試類。對(duì)Timeout來說,如果不是在測(cè)試類中所有的測(cè)試方法都需要有時(shí)間限制,我并不推薦適用Timeout;對(duì)ExpectedException它使用了hamcrest中的Matcher來匹配,因而提供了更強(qiáng)大的控制能力,但是一般的使用,感覺@Test中的expected字段就夠了,它多次調(diào)用expected表達(dá)是and的關(guān)系,即如果我有兩個(gè)Exception,則拋出的Exception必須同時(shí)是這兩個(gè)類型的,感覺沒有什么大的意義,因而我不怎么推薦使用這個(gè)Rule,關(guān)于hamcrestMather框架將在Assert小節(jié)中詳細(xì)介紹。這兩個(gè)Rule原本就是基于測(cè)試方法設(shè)計(jì)的,因而如果應(yīng)用在@ClassRule上好像沒有什么大的意義,不過Timeout感覺是可以應(yīng)用在@ClassRule中的,如果要測(cè)試一個(gè)測(cè)試類整體運(yùn)行時(shí)間的話,當(dāng)然如果存在這種需求的話。

           1 public class Timeout implements TestRule {
           2     private final int fMillis;
           3     public Timeout(int millis) {
           4        fMillis= millis;
           5     }
           6     public Statement apply(Statement base, Description description) {
           7        return new FailOnTimeout(base, fMillis);
           8     }
           9 }
          10 public class ExpectedException implements TestRule {
          11     public static ExpectedException none() {
          12        return new ExpectedException();
          13     }
          14     private Matcher<Object> fMatcher= null;
          15     private ExpectedException() {
          16     }
          17     public Statement apply(Statement base,
          18            org.junit.runner.Description description) {
          19        return new ExpectedExceptionStatement(base);
          20     }
          21     public void expect(Matcher<?> matcher) {
          22        if (fMatcher == null)
          23            fMatcher= (Matcher<Object>) matcher;
          24        else
          25            fMatcher= both(fMatcher).and(matcher);
          26     }
          27     public void expect(Class<? extends Throwable> type) {
          28        expect(instanceOf(type));
          29     }
          30     public void expectMessage(String substring) {
          31        expectMessage(containsString(substring));
          32     }
          33     public void expectMessage(Matcher<String> matcher) {
          34        expect(hasMessage(matcher));
          35     }
          36     private class ExpectedExceptionStatement extends Statement {
          37        private final Statement fNext;
          38        public ExpectedExceptionStatement(Statement base) {
          39            fNext= base;
          40        }
          41        @Override
          42        public void evaluate() throws Throwable {
          43            try {
          44               fNext.evaluate();
          45            } catch (Throwable e) {
          46               if (fMatcher == null)
          47                   throw e;
          48               Assert.assertThat(e, fMatcher);
          49               return;
          50            }
          51            if (fMatcher != null)
          52               throw new AssertionError("Expected test to throw "
          53                      + StringDescription.toString(fMatcher));
          54        }
          55     }
          56     private Matcher<Throwable> hasMessage(final Matcher<String> matcher) {
          57        return new TypeSafeMatcher<Throwable>() {
          58            public void describeTo(Description description) {
          59               description.appendText("exception with message ");
          60               description.appendDescriptionOf(matcher);
          61            }
          62            @Override
          63            public boolean matchesSafely(Throwable item) {
          64               return matcher.matches(item.getMessage());
          65            }
          66        };
          67     }
          68 }

           

          RuleChain

          RuleChain提供一種將多個(gè)TestRule串在一起執(zhí)行的機(jī)制,它首先從outChain()方法開始創(chuàng)建一個(gè)最外層的TestRule創(chuàng)建的Statement,而后調(diào)用round()方法,不斷向內(nèi)層添加TestRule創(chuàng)建的Statement。如其注釋文檔中給出的一個(gè)例子:

          1 @Rule
          2 public TestRule chain= RuleChain
          3                     .outerRule(new LoggingRule("outer rule"))
          4                     .around(new LoggingRule("middle rule"))
          5                     .around(new LoggingRule("inner rule"));

           

          如果LoggingRule只是類似ExternalResource中的實(shí)現(xiàn),并且在before()方法中打印starting…,在after()方法中打印finished…,那么這條鏈的執(zhí)行結(jié)果為:

          starting outer rule
          starting middle rule
          starting inner rule
          finished inner rule
          finished middle rule
          finished outer rule

           

          由于TestRuleapply()方法是根據(jù)的當(dāng)前傳入的Statement,創(chuàng)建一個(gè)新的Statement,以決定當(dāng)前TestRule邏輯的執(zhí)行位置,因而第一個(gè)調(diào)用apply()TestRule產(chǎn)生的Statement將在Statement鏈的最里面,也正是有這樣的邏輯,所以around()方法實(shí)現(xiàn)的時(shí)候,都是把新加入的TestRule放在第一個(gè)位置,然后才保持其他已存在的TestRule位置不變。

           1 public class RuleChain implements TestRule {
           2     private static final RuleChain EMPTY_CHAIN= new RuleChain(
           3            Collections.<TestRule> emptyList());
           4     private List<TestRule> rulesStartingWithInnerMost;
           5     public static RuleChain emptyRuleChain() {
           6        return EMPTY_CHAIN;
           7     }
           8     public static RuleChain outerRule(TestRule outerRule) {
           9        return emptyRuleChain().around(outerRule);
          10     }
          11     private RuleChain(List<TestRule> rules) {
          12        this.rulesStartingWithInnerMost= rules;
          13     }
          14     public RuleChain around(TestRule enclosedRule) {
          15        List<TestRule> rulesOfNewChain= new ArrayList<TestRule>();
          16        rulesOfNewChain.add(enclosedRule);
          17        rulesOfNewChain.addAll(rulesStartingWithInnerMost);
          18        return new RuleChain(rulesOfNewChain);
          19     }
          20     public Statement apply(Statement base, Description description) {
          21        for (TestRule each : rulesStartingWithInnerMost)
          22            base= each.apply(base, description);
          23        return base;
          24     }
          25 }

           

          TestRuleStatement的運(yùn)行

          TestRule實(shí)例的運(yùn)行都是被封裝在一個(gè)叫RunRulesStatement中運(yùn)行的。在構(gòu)造RunRules實(shí)例是,傳入TestRule實(shí)例的集合,然后遍歷所有的TestRule實(shí)例,為每個(gè)TestRule實(shí)例調(diào)用一遍apply()方法以構(gòu)造出要執(zhí)行TestRuleStatement鏈。類似上小節(jié)的RuleChain,這里在前面的TestRule構(gòu)造的Statement被是最終構(gòu)造出的Statement的最里層,結(jié)合TestClass在獲取注解字段的順序時(shí),先查找子類,再查找父類,因而子類的TestRule實(shí)例產(chǎn)生的Statement是在Statement鏈的最里層,從而保證了類似ExternalResource實(shí)現(xiàn)中,before()方法的執(zhí)行父類要比子類要早,而after()方法的執(zhí)行子類要比父類要早的特性。

           1 public class RunRules extends Statement {
           2     private final Statement statement;
           3     public RunRules(Statement base, Iterable<TestRule> rules, Description description) {
           4        statement= applyAll(base, rules, description);
           5     }
           6     @Override
           7     public void evaluate() throws Throwable {
           8        statement.evaluate();
           9     }
          10     private static Statement applyAll(Statement result, Iterable<TestRule> rules,
          11            Description description) {
          12        for (TestRule each : rules)
          13            result= each.apply(result, description);
          14        return result;
          15     }
          16 }

           

          posted on 2012-05-12 00:02 DLevin 閱讀(7093) 評(píng)論(0)  編輯  收藏 所屬分類: JUnit
          主站蜘蛛池模板: 枣强县| 汝州市| 洪江市| 汨罗市| 浦北县| 景谷| 隆德县| 平山县| 新兴县| 剑阁县| 巴青县| 柏乡县| 平乐县| 彭州市| 邵东县| 屏边| 克拉玛依市| 漾濞| 阳东县| 海安县| 林周县| 竹山县| 安乡县| 饶阳县| 淅川县| 德令哈市| 湖州市| 略阳县| 肥东县| 株洲县| 襄垣县| 惠东县| 尼木县| 晋城| 吉林市| 东安县| 巴中市| 昌平区| 辽中县| 陈巴尔虎旗| 莱芜市|