【TestNG快板說九】如何使用@Factory進行測試類的多樣化執行

開始,我們先來思考一個場景,有一個測試類需要根據測試傳入的N個參數進行N次執行。

方式一,可以直接在suite.xml中設置多個class,輸入不同的參數執行測試用例

xml配置:

<suite name="Factory Test" verbose="1">
    <test name="Regression1">
        <parameter name="key" value="1"/>
        <classes>
            <class name="com.local.testng.SimpleTest">
            </class>
        </classes>
    </test>
    <test name="Regression2">
        <parameter name="key" value="2"/>
        <classes>
            <class name="com.local.testng.SimpleTest">
            </class>
        </classes>
    </test>
</suite>

運行兩次測試用例,第一次傳key=“1”,第二次傳key=2

測試用例:

public class SimpleTest {
    private static Logger logger = null;

    @BeforeTest
    public void setUp() {
        logger = Logger.getLogger(WebTest.class);
    }

    @Parameters({"key"})
    @Test
    public void testServer(String key) {
        logger.info(">>> "+key);
    }
}

執行結果:

[WebTest] [INFO] >>> 1
[WebTest] [INFO] >>> 2

===============================================
Factory Test
Total tests run: 2, Failures: 0, Skips: 0
===============================================

從執行結果上看,測試用例確實使用了不同參數運行了兩次,但是這裏有明顯的缺點:

  1. xml配置參數只能是String類型
  2. 需要配置多<test>,非常冗餘和不好管理

方式二,使用@Factory來配置工廠類來動態傳入不同參數運行同一個測試用例

預執行的Test:

public class WebTest {
    private int m_numberOfTimes;
    private static Logger logger = null;

    public WebTest(int numberOfTimes) {
        m_numberOfTimes = numberOfTimes;
    }

    @BeforeTest
    public void setUp() {
        logger = Logger.getLogger(WebTest.class);
    }

    @Test
    public void testServer() {
        logger.info(">>> "+m_numberOfTimes);
    }
}

需要多次執行testServer,輸出從0~2的值,既m_numberOfTimes依次取值0,1,2輸出,我們使用Factory來調用這個測試類:

public class WebTestFactory {

    @Factory
    public Object[] createInstance() {
        Object[] result = new Object[3];
        for (int i = 0; i < 3; i++) {
            result[i] = new WebTest(i);
        }
        return result;
    }
}

上面用Factory註釋createInstance,返回Object類型數組,循環生成了三個WebTest的對象賦值其中,然後在xml中調用這個class類:

<suite name="Factory Test" verbose="1">
    <test name="Regression1">
        <!--<parameter name="key" value="1"/>-->
        <classes>
            <class name="com.local.testng.WebTestFactory">
            </class>
        </classes>
    </test>
</suite>

結果輸出:

[WebTest] [INFO] >>> 2
[WebTest] [INFO] >>> 1
[WebTest] [INFO] >>> 0

===============================================
Factory Test
Total tests run: 3, Failures: 0, Skips: 0
===============================================

看到這裏,仔細的同學可能會想,你這createInstance什麼鬼,循環三次還不是寫死,哪來的動態?確實,我們來稍微修改下:

public class WebTestFactory {

    @Parameters({"key"})
    @Factory
    public Object[] createInstance(String value) {
        Integer num = Integer.valueOf(value);
        Object[] result = new Object[num];
        for (int i = 0; i < num; i++) {
            result[i] = new WebTest(i);
        }
        return result;
    }
}

修改完後,取xml中配置參數key的值傳入createInstance,比如現在我們想輸出0~4,只要改下配置文件參數值即可:

<suite name="Factory Test" verbose="1">
    <test name="Regression1">
        <parameter name="key" value="5"/>
        <classes>
            <class name="com.local.testng.WebTestFactory">
            </class>
        </classes>
    </test>
</suite>

輸出結果:

[WebTest] [INFO] >>> 3
[WebTest] [INFO] >>> 2
[WebTest] [INFO] >>> 0
[WebTest] [INFO] >>> 4
[WebTest] [INFO] >>> 1

===============================================
Factory Test
Total tests run: 5, Failures: 0, Skips: 0
===============================================

可能到這裏,還是有同學會說,我的業務數據很複雜,可不是什麼123之類,可能就是數據庫裏查出來的一推業務數據。沒問題,接着我們來看看當FactoryDataProvider結合在一起,會產生什麼效果。

先看一個DataProvider:

public class WebTestDataProvider {

    @DataProvider(name = "provider1")
    public static Object[][] provider1() {
        Object[][] provider = new Object[1][3];
        HashMap<String, String> one = new HashMap<String, String> () {
            {
                put("one", "1");
            }
        };
        HashMap<String, String> two = new HashMap<String, String> () {
            {
                put("two", "2");
            }
        };
        return new Object[][] {
                {1, "one", one},
                {2, "two", two}
        };

    }
}

這裏順道講下DataProvider的使用,分兩種:

  1. 直接在測試類裏面定義DataProvider,只能用於當前測試類
  2. 定義在一個專門的Class中,被其他測試類引用,需要注意的是如果是單獨定義一個Class來放置DataProvider,對應的DataProvider方法需要定義爲static,引用的時候還要指明具體類,這裏我們就使用這第二種方式。

定義Factory的類中的引用DataProvider

public class WebTestFactory {

    @Factory(dataProvider = "provider1", dataProviderClass = com.local.testng.WebTestDataProvider.class)
    public Object[] createInstance(Integer index, String name, HashMap<String, String> content) {
        return new Object[] {new FactoryAndProviderTest(index, name, content)};
    }
}

這裏注意一點,createInstance的傳參需要合DataProvider返回的二維數據的每一行的數據一致,即個數、順序、類型都要一致,否則testng無法解析。

實際執行的測試類:

public class FactoryAndProviderTest {
    private Integer index;
    private String name;
    private HashMap<String, String> content;
    private static Logger logger = null;

    public FactoryAndProviderTest(Integer index, String name, HashMap<String, String> content) {
        this.index = index;
        this.name = name;
        this.content = content;
    }

    @BeforeTest
    public void setUp() {
        logger = Logger.getLogger(FactoryAndProviderTest.class);
    }

    @Test
    public void testServer() {

        logger.info("testServer>>> "+this.index);
        logger.info("testServer>>> "+this.name);
        logger.info("testServer>>> "+this.content.toString());
    }

    @Test
    public void testServer1() {

        logger.info("testServer1>>> "+this.index);
        logger.info("testServer1>>> "+this.name);
        logger.info("testServer1>>> "+this.content.toString());
    }
}

suite配置:

<suite name="Factory Test" verbose="1">
    <test name="Regression1">
        <parameter name="key" value="5"/>
        <classes>
            <class name="com.local.testng.WebTestFactory">
            </class>
        </classes>
    </test>
</suite>

運行後,FactoryAndProviderTest中的所有測試類都會循環使用DataProvider中的數據,執行程序的邏輯。

測試結果:

[FactoryAndProviderTest] [INFO] testServer>>> 2
[FactoryAndProviderTest] [INFO] testServer>>> two
[FactoryAndProviderTest] [INFO] testServer>>> {two=2}
[FactoryAndProviderTest] [INFO] testServer>>> 1
[FactoryAndProviderTest] [INFO] testServer>>> one
[FactoryAndProviderTest] [INFO] testServer>>> {one=1}
[FactoryAndProviderTest] [INFO] testServer1>>> 2
[FactoryAndProviderTest] [INFO] testServer1>>> two
[FactoryAndProviderTest] [INFO] testServer1>>> {two=2}
[FactoryAndProviderTest] [INFO] testServer1>>> 1
[FactoryAndProviderTest] [INFO] testServer1>>> one
[FactoryAndProviderTest] [INFO] testServer1>>> {one=1}

===============================================
Factory Test
Total tests run: 4, Failures: 0, Skips: 0
===============================================

講到這裏,大家可能想到當@DataProvider註釋某一個TestCase的時候,測試用例也會使用所有數據跑一遍程序邏輯,然後比較下當@Factory和@DataProvider結合,它可以讓某個測試類中的所有方法都遍歷DataProvider中的數據執行一遍程序邏輯,可以更方便的規劃測試用例地執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章