.netcore持續集成測試篇之web項目驗收測試

系列目錄css

經過前面的單元測試,咱們可以保證項目的基本模塊功能邏輯是正常的,經過集成測試可以保證接口的請求是正常的.然而最終項目交付咱們還須要對項目進行頁面的行爲進行測試,好比頁面佈局是否正常,按鈕是否能點擊,點擊後執行的動做是否正確,連接是否正常等功能進行測試,表單提交是否返回正確結果等.這些都是一些墨盒測試,通常是由專門測試人員來完成,然而隨着web的發展,各類自動化工具愈來愈完善,有一些頁面功能的測試也能夠由程序員來編寫自動測試代碼完成.這裏主要結合Selenium來介紹如何完成頁面行爲的測試.html

點擊按鈕

前面咱們已經講到如何安裝和簡單使用Selenium,這裏再也不介紹.下面介紹一下如何使用Selenium來觸發一個按鈕點擊事件.jquery

首先咱們在HelloWorldController裏新建Action FormTest(也能夠在其它控制器裏建立,這裏隨意),代碼以下程序員

public IActionResult FormTest()
        {
            return View();
        }
        [HttpPost]
        public IActionResult FormTest(string name)
        {
            return Content(name);
        }

以上代碼很是簡單,咱們建立FormTest並請求本身,而後把請求的數據返回web

咱們爲這個Action新建一個頁面,而且引入jquery.ajax

頁面代碼以下api

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <script src="~/js/JQueryt.js"></script>
    <title>FormTest</title>
</head>
<body>
<form method="post" id="frm1">
    <input id="btn1" type="button" value="點我"/>
</form>
<script>
    $("#btn1").click(function() {
        alert("hello,world");
    });
</script>
</body>
</html>

這個頁面裏有一個btn1,若是咱們點擊它就會彈出一個alert框.
測試代碼以下瀏覽器

[Fact]
        public void ClickTest()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);

            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var element = driver.FindElement(By.Id("btn1"));
            element.Click();
        }

咱們先經過id找到這個按鈕,而後令它觸發一個click事件.咱們運行測試服務器

avatar

咱們並無手動點擊按鈕,可是彈出了上面的彈框,說明點擊事件正確觸發了.cookie

自動填寫表單

經過以上代碼咱們能夠看到,觸發一個按鈕點擊事件在Selenium是很是容易的,這對咱們自動模擬表單提交提供了大大的便利.Selenium還能夠模擬自動填寫表單,思路和上面是同樣的,首先獲取到要填寫的表單,而後模擬填寫內容.下面咱們改動一下網頁代碼,在form裏面添加一個簡單的表單

<input type="text" name="name"/>

測試代碼改成以下:

[Fact]
        public void ClickTest()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);

            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var input = driver.FindElement(By.Name("name"));
            input.SendKeys("hello,world");
        }

以上代經過FindElement By.Name獲取到頁面裏name爲name的元素(聽起來有點繞),而後經過SendKeys方法模擬向指定元素填寫內容

avatar

頁面的開之後便會自動填寫以上內容.這樣咱們就能夠自動填寫內容,而後點擊點我按鈕提交表單了.

自動填寫表單,而後提交

綜合以上咱們模擬一次自動填寫表單,而後提交的動做.

下面貼出修改後的完整代碼.

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <script src="~/js/JQueryt.js"></script>
    <title>FormTest</title>
</head>
<body>
<form method="post" id="frm1">
    <input type="text" name="name"/>
    <input id="btn1" type="button" value="點我"/>
</form>
<script>
    $("#btn1").click(function() {
        $.ajax({
            type: "POST",
            url: "/HelloWorld/FormTest",
            data: $("#frm1").serialize(),
            dataType: "text",
            success: function (response) {
                alert("返回的數據是:"+response);
            }
        });
    });
</script>
</body>
</html>

此次當按鈕點擊之後咱們觸發一次ajax提交,而後alert服務器返回的數據

測試代碼以下:

[Fact]
        public void ClickTest()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);

            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var input = driver.FindElement(By.Name("name"));
            input.SendKeys("hello,world");
            var btn = driver.FindElement(By.Id("btn1"));
            btn.Click();
        }

上面的代碼執行了兩個動做,第一是模擬填寫表單數據,第二是點擊按鈕,提交表單.
咱們運行測試代碼,看一下結果

avatar

能夠看到表單自動提交了.

獲取Alert框

咱們前面都是經過截圖來看指定的行爲是否產生了正確的結果,然而在自動化環境中這是不能接受的,更多的時候咱們是在無頭模式下進行測試,而後自動獲取行爲產生的結果,而後斷言此結果是不是期待的一個值.下面咱們改造以上代碼,自動獲致到Alert框並取得它裏面的值,而後斷言這個值是咱們想要的值.

[Fact]
        public void ClickTest()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);

            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var input = driver.FindElement(By.Name("name"));
            input.SendKeys("hello,world");
            var btn = driver.FindElement(By.Id("btn1"));
            btn.Click();
            Thread.Sleep(3000);
            var alert = driver.SwitchTo().Alert();
            var txt = alert.Text;
            Assert.Equal("返回的數據是:hello,world", txt);
        }

以上代碼的關鍵是經過SwitchTo獲取到Alert框,進而獲取到它的Text值,咱們在ajax請求成功的處理是"返回的數據是:"+提交的值,所以若是正常則以上代碼會執行成功.這樣咱們就不用守着頁面查看結果了.

獲取自定義彈出層

作到以上並無成事大吉,實際業務中咱們不多使用瀏覽器自帶的Alert,而是使用一些第三方的組件,由於原生Alert用戶體驗實在不是太好,只能在測試的時候玩一玩還能夠.因爲第三方組件實現方式不一樣,這就致使獲取的方法也不同,咱們還要根據具體狀況而定.下面咱們結合layui的alert框來介紹一下如何來獲取它裏面的內容.

咱們在項目中引入layui,而後把ajax請求成功後的alert換成layui的alert,代碼以下

<html>
<head>
    <meta name="viewport" content="width=device-width" />

    <link href="~/lib/layui/css/layui.css" rel="stylesheet" />
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/layui/layui.all.js"></script>
    <title>FormTest</title>
</head>
<body>
<form method="post" id="frm1">
    <input type="text" name="name"/>
    <input id="btn1" type="button" value="點我"/>
</form>
<script>
    var layer = layui.layer;
    $("#btn1").click(function() {
        $.ajax({
            type: "POST",
            url: "/HelloWorld/FormTest",
            data: $("#frm1").serialize(),
            dataType: "text",
            success: function (response) {
                layer.alert("返回的數據是:" + response);
            }
        });
    });
</script>
</body>
</html>

因爲這是一個自定義alert,咱們先運行一下項目,而後手動點擊下按鈕,等alert框出來之後咱們分析一下它的結構:

avatar

咱們能夠看到,layui的這個alert框其實是一個div層,因爲id是動態生成的,所以咱們不能使用,可是它的class是固定的,它包含了兩個class元素,內部彈出的具體內容則是藍色高亮的那個div裏面的內容,它的class也是固定的,咱們這裏可使用class獲取到它們.

下面看測試代碼:

[Fact]
        public void ClickTest()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);

            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var input = driver.FindElement(By.Name("name"));
            input.SendKeys("hello,world");
            var btn = driver.FindElement(By.Id("btn1"));
            btn.Click();
            Thread.Sleep(3000);
            var layer = driver.FindElement(By.ClassName("layui-layer-dialog"));
            var content = layer.FindElement(By.ClassName("layui-layer-content"));
            var text = content.Text;
            Assert.Equal("返回的數據是:hello,world", text);
        }

產生咱們經過class獲取到這個彈出層元素,而後再經過它找到它的子元素(包含彈出信息文字的div).這裏的sleep前面說過,因爲js是異步執行的,所以點擊後並不能立刻獲取到結果,這裏咱們sleep一下.

須要特別注意的是,經過By.ClassName獲取到的元素可能不止一下,默認取得的是獲取到的第一個,這在有些時候可能並不能知足咱們的要求(這裏代碼比較少,發生衝突的機率比較小),實際工做中咱們必定要想辦法保證元素的唯一性,也就是獲取到的元素肯定是咱們所須要的.

還有一點須要注意的是第三方的組件實現方式可能會改變致使獲取不到內容,這確實沒有比較好的解決方案.

實際工做中可能還會有更爲複雜的行爲要去模擬,好比說彈出層是一個帶有tab的面板,咱們須要切換到特定的tab去尋找想要的內容,因爲這些內容都是非標準實現,所以模擬的難度根據採用框架的複雜度而定,有時候可能特別複雜,可是隻要靜下心來分析分析,老是能找到解決方案的.

前面講到了如何填寫表單,點擊按鈕提交表單以及獲取彈出層內容.下面講解一下如何獲取連接,彈出頁面,iframe以及高級行爲.這裏仍然是以實際應該爲主導講解一些最基本最經常使用的功能,並不求面面俱到,有興趣的同事能夠查看官方文檔,第三方博客,書籍等獲取更多知識.

連接行爲測試

連接不少時候能夠完成按鈕的功能,可是最經常使用的是跳到一個新的頁面,下面講一下如何獲取到新的頁面

咱們在上節的頁面中添加一個a連接,代碼以下:

<a id="clk" href="http://www.baidu.com" target="_blank">飛往百度</a>

以上代碼很簡單,點擊一下a標籤就會出現一個新的百度頁面,咱們想要判斷一下是否正確打開了百度頁面,測試代碼以下:

[Fact]
        public void LinkClick()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);

            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var link = driver.FindElement(By.Id("clk"));
            link.Click();
            var hands = driver.WindowHandles;
            var wind = driver.SwitchTo().Window(hands[1]);
            var title = wind.Title;
            Assert.Equal("百度一下,你就知道", title);
        }

上面代碼主要的功能在於當點擊連接之後經過driver.WindowHandles獲取到容器的句柄,須要說明的是這裏的句柄並非指針類型的句柄,而是一個字符串類類型的變量,咱們能夠經過它找到指定的窗口,下面部分經過SwitchTo切換到一個窗口(SwitchTo咱們前面講到過),Window接收一個字符串類開的參數,雖然提示字符說是窗口的標題,實際上並非,而是咱們剛纔獲取到的句柄,咱們知道如今共有兩個窗口,百度窗口是後打開的,所以它的索引是是1.而後咱們再獲取它的標題,看看是否是"百度一下, 你就知道"

須要說明的是,以上咱們雖然是經過索引獲取的百度窗口,這樣可能會由於位置切換形成問題(這裏強烈不建議手動修改自動過程當中的行爲,實際上真實的測試環境是無頭環境,所以這其實不是一個很大的問題),就上面的例子咱們確實有辦法能唯一肯定百度窗口,可是若是窗口過多想要不使用索引獲取到指定的窗口仍是很困難的,這裏強烈建議若是有打開很是多的窗口的複雜行爲時,把測試分紅若干個測試,每一個測試裏的邏輯只打開少許窗口,這樣出現問題也更容易排查.

點擊時按下修改鍵

前面咱們屢次用到了模擬點擊事件,其實這也是實際項目中用的最多的,可是也不排除少數狀況下會用到其它的按鍵,好比說拖拽,雙擊,ctrl+點擊等,

下面咱們演示如何在百度首頁點擊百度新聞並在新頁面打開,咱們知道百度首頁的新聞默認是在本頁打開的,若是點擊連接時按下ctrl鍵則會在新頁面中打開.下面咱們模擬ctrl+點擊這個行爲
這裏其實也很簡單,主要經過Actions封裝對象來觸發一系列動做來達到咱們的目的.

下面看測試代碼:

[Fact]
        public void LinkClick()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);

            driver.Url = "http://www.baidu.com";
            driver.Navigate();
            var link = driver.FindElement(By.LinkText("新聞"));
            Actions actions = new Actions(driver);
            actions.KeyDown(Keys.LeftControl).Click(link).Build().Perform();
        }

以上代碼主要是經過傳入driver對象構造一個Actions類型對象,這個對象在調用build以前會一直返回自身,相似是是jQuery裏的鏈式操做,這樣咱們就能夠連續執行多個動做.

下面的代碼咱們先是調用actions對象的keyDown方法,而後傳入要按下的鍵,而後再調用點擊事件,最後調用Build方法終止鏈式調用,最後再執行Perform執行前面的操做.啓動測試就會發現瀏覽器在新的頁面打開了百度新聞頁

上面用到了一個之前沒用到的選擇方法那就是By.LinkText,語義很是明確那就是根據連接的文本找到連接對象.

Iframe對象的獲取

咱們知道Iframe對象的處理比較麻煩,裏面是一個比較封裝的區域與外面通訊過程比較麻煩,在selenium裏它的處理也比較特殊,直接按照id或者其它特徵獲取到它幾乎沒有任何做用,由於沒法獲取到內容元素,selenium是經過switchTo.Frame傳入獲取到的iframe對象對它進行一層封裝,而後就可以正常獲取到它內部的元素了.

咱們在頁面添加一個簡單的iframe頁面,代碼以下

<iframe id="ifrm1" src="http://www.baidu.com"></iframe>

測試代碼以下

[Fact]
        public void FindIframe()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);
            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var ele = driver.FindElement(By.Id("ifrm1"));
            var frm = driver.SwitchTo().Frame(ele);
            var txt = frm.FindElement(By.LinkText("新聞")).Text;
            Assert.Equal("新聞", txt);
        }

以上代碼首先像獲取普通元素同樣獲取到這個iframe對象,而後經過SwitchTo.Frame把它傳入封裝成個frame對象,後面就能夠獲取到它內部的元素了.(iframe指向百度首頁,咱們獲取到新聞連接)

然而實際項目中,每每咱們並不本身去建立iframe,而是由一些第三方ui框架自動建立的,框架生成的iframe要麼沒有id,要麼是動態的,所以使用自動生成元素的id要很是慎重,可是筆者見過很多在生成iframe時能夠給iframe指定name的,因爲頁面中iframe通常都不會太多,咱們能夠給它命一個唯一的名字,經過名字找到它.若是沒有名字,還能夠根據它的class找到它,通常iframe樣式class都是固定的,可是這時候要想辦法確保選擇到的對象是唯一的,這樣才能保證測試結果的穩定性.

獲取下拉列表

在一些查詢功能中,每每全有下拉列表,經過js獲取或者設置下拉項並非一件很困難的事,然而咱們並不想不了測試而增長無關的js代碼,這樣用完還要刪除很是麻煩,其實Selenium也提供了設置下拉列表選項的功能,這樣極大方便了咱們的測試.

下面看示例代碼

咱們首先 在頁面中添加以下代碼

<select name="China" id="zhengzhouDistrict">
    <option value="">--請選擇區域--</option>
    <option value="pdditrict">中原區</option>
    <option value="hpditrict">二七區</option>
    <option value="xhditrict">管城區</option>
    <option value="cnditrict">高新區</option>
    <option value="sjditrict">開發區</option>
</select>

測試代碼以下:

[Fact]
        public void DropDownListTest()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);
            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var element = driver.FindElement(By.Id("zhengzhouDistrict"));
            SelectElement dropdownList = new SelectElement(element);
            dropdownList.SelectByIndex(3);
            var select = dropdownList.SelectedOption.Text;
            Assert.Equal("管城區", select);
        }

以上代碼中咱們先是經過普通方法獲取到了這個下拉列表,而後把它封裝成一個SelectElement對象,而後調用它的SelectByIndex設置選中的項,這樣選中的項就是不默認值了,而是咱們想要選擇的值.

select還有按索引,鍵,值等設置選擇項的方法,而且能夠取消選擇,你們本身嘗試一下,這裏再也不介紹.

然而以上方法並無什麼太大做用,因爲瀏覽器自帶的select界面每每都不太美觀,而且動態交互性不是很是好,實際項目中咱們不多使用原生的select,而是使用第三方ui框架帶的select,而第三方框架每每都是把select隱藏起來,而後把它的值賦值給一個input元素,它設置和獲取值都是經過第三方框架提供的api而非原生select自帶的方法.若是這時候使用以上方法獲取select元素就會致使失敗,selenium會提交元素被隱藏沒法交互.針對這個問題筆者採用了一種比較笨的方法那就是模擬按鍵,固然這裏模擬按鍵並不引入第三方按鍵類框架,而是使用selenium自己的功能.

下面仍然以layui爲例說明如何設置下拉列表值.

頁面代碼以下

<html>
<head>
    <meta name="viewport" content="width=device-width" />

    <link href="~/lib/layui/css/layui.css" rel="stylesheet" />
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/layui/layui.all.js"></script>
    <title>FormTest</title>
</head>
<body>
<form class="layui-form" action="">
    <div class="layui-form-item">
        <div class="layui-input-inline">
            <select name="China" id="zhengzhouDistrict">
                <option value="">--請選擇區域--</option>
                <option value="pdditrict">中原區</option>
                <option value="hpditrict">二七區</option>
                <option value="xhditrict">管城區</option>
                <option value="cnditrict">高新區</option>
                <option value="sjditrict">開發區</option>
            </select>
        </div>
    </div>
</form>
        <script>
            var layer = layui.layer;
            var form = layui.form;
            form.render();
        </script>
</body>
</html>

以上代碼和上面的相似,只是這裏把它封裝成layui的select,咱們的思路是先獲取到layui的顯示select的元素,也就是最終渲染的input元素,通過觀察發現這個元素有一個這樣的樣式layui-input,咱們能夠經過這個關鍵信息找到它,而後點擊一下,這時候下拉列表就出來了,此時再點擊向下按鈕,在想要的位置處click一下就能夠獲得想要的結果了.

測試代碼以下:

[Fact]
        public void DropDownListTest()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);
            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var input = driver.FindElement(By.ClassName("layui-input"));
            input.Click();
            Actions action = new Actions(driver);
            action.SendKeys(Keys.ArrowDown+Keys.ArrowDown+Keys.ArrowDown).Click().Build().Perform();
        }

以上主要是使用的Actions連續點擊兩次向下,就能夠選擇到指定的元素了.

日期框處理

這裏結合layui來說解如何處理日期框的問題

首先咱們來觀察一下layui日期輸入框的特色,它實際上是一個input,而且是能夠接受用戶輸入的,這就跟咱們模擬手動輸入帶來了方便,可是事情並無這麼簡單,咱們能夠看到手輸內容以後還要點擊那個彈出層的肯定按鈕來確認輸入,一旦點擊了肯定則會把彈出層默認選中的日期輸入到input框中,覆蓋了剛纔的選擇.然而它卻有如下一個特色:若是咱們輸入之後不點擊確認,而是把光標移到空白色點擊或者光標焦點移到其它可輸入元素內,則也能夠確認輸入.這樣咱們就能夠在日期輸入框輸入內容之後再把焦點移到其它輸入框就可以確認輸入了.

頁面代碼以下:

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <link href="~/lib/layui/css/layui.css" rel="stylesheet" />
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/layui/layui.all.js"></script>
    <title>FormTest</title>
</head>
<body>
<div class="layui-inline">
    <input type="text" class="layui-input" id="test1">
    <input type="text" id="input1"/>
</div>
<script>
    var laydate = layui.laydate;
    laydate.render({
        elem: '#test1'
    });
</script>
</body>
</html>

以上有兩個input框,一個是普通input,一個是日期框,咱們模擬在日期輸入框輸入內容後把焦點移動它右邊空白處點擊,而後看看上面日期輸入框裏的值是否是咱們賦予的.

測試代碼以下:

[Fact]
        public void DropDownListTest()
        {
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory);
            driver.Url = "http://localhost:28614/HelloWorld/FormTest";
            driver.Navigate();
            var input = driver.FindElement(By.Id("test1"));
            input.SendKeys("2008-5-3");
            var position = input.Location;
            var clickposition = position.X + 200;
            Actions actions = new Actions(driver);
            actions.MoveByOffset(clickposition, 0).Click().Build().Perform();
            var txt = input.GetAttribute("value");
            Assert.Equal("2008-05-03", txt);
        }

以上代碼咱們首先獲取到這個日期選擇框,而後給它輸入值.下面咱們獲取它的位置主要是爲了讓它關閉(若是點擊了空白處或者其它可點擊控件,則日期選擇框就會消失,這裏只因此要關閉它是由於它當前處於激活狀態,若是不關閉則會影響其它操做).咱們獲取到它的位置後向右移動鼠標到空白處,而後點擊空白處日期選擇框就消失了.咱們輸入的是'2008-5-3'而斷言它是'2008-05-03'是由於layui有格式糾正功能,自動把一位的數據前面補零.

這裏獲取input的值是經過GetAttribute獲取的,而不是經過Text,text是獲取元素內部的文本(也就是文本包含在標籤裏),而input的值是它的value屬性的一個值,所以使用Text獲取不到.
必定要注意點擊位置,若是點擊位置位於連接或者提交按鈕上則可能觸發不可預期的效果.

前面咱們介紹瞭如何經過普通的方法給元素設置值以及模擬特定的行爲,本篇主要介紹如何獲取頁面cookie,如何模擬手機測試.

獲取頁面cookie

有些比較複雜的測試可能會用到cookie,在Selenium裏經過driver.Manage().Cookies就能夠獲取到頁面全部的cookie對象了.

模擬手機瀏覽器

因爲目前沒有手機項目,這裏並不詳細介紹,只是做爲一個知識點簡單介紹一下.

看如下測試代碼

[Fact]
        public void DropDownListTest()
        {
            ChromeOptions opts = new ChromeOptions();
            opts.EnableMobileEmulation("iPhone X");
            IWebDriver driver =
                new ChromeDriver(Environment.CurrentDirectory,opts);
            driver.Url = "http://www.baidu.com";
            driver.Navigate();

        }

這裏主要是增長一個谷歌瀏覽器啓用模擬手機瀏覽器選項,並指定一個模擬器的名字(這些名字能夠經過谷歌瀏覽器的手機模式查看).而後再啓動頁面就會在指定的手機模擬器運行了.

以上運行結果截圖以下:

avatar

相關文章
相關標籤/搜索