.netcore持續整合測試篇之Xunit結合netcore記憶體伺服器傳送post請求

周國通發表於2019-08-14

系列目錄

.net core整合測試之Post請求

Web專案中,很多與使用者資料互動的請求都是Post請求,想必大家都用過HttpClient構造過post請求,這裡並不對HttpClient做詳細介紹,只介紹一些常用的功能.並結合AutoFixture演示如何自動構造請求資料,簡單手動建立Json或者Formdata的工作量,提高生產效率.

我們為上節建立的HelloWorld控制器新增一個StudentInfo方法,內容如下

       [HttpPost]
        public IActionResult StudentInfo(Student student)
        {
            return Content(student.Name);
        }

這個方法的引數是一個Student型別的物件,和早期版本mvc並沒有太大差別,我們把這個Student類的程式碼貼出來:

 public class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public byte Gender { get; set; }
        public string School { get; set; }
    }

下面我們來構造一個對StudentInfo的請求測試方法.

如何建立測試記憶體伺服器的方法在第8節裡有講到,這裡不再講,以下用到的都是測試伺服器建立的HttpClient物件.

       [Fact]
        public async Task SimplePost()
        {
            Dictionary<string, string> dict = new Dictionary<string, string>
            {
                {"name","sto"},
                {"age","32" },
                {"gender","2" },
                {"school","middleschool" }
            };
            var response = await _client.PostAsync("/HelloWorld/StudentInfo", new FormUrlEncodedContent(dict));
            response.EnsureSuccessStatusCode();
            var result =await response.Content.ReadAsStringAsync();
            Assert.Equal("sto", result);
        }

以上程式碼可能大家都非常熟悉了.我們用dictionary物件構造了formdata然後做為post請求內容傳送到服務端,服務端返回的是接收到的student物件的name屬性,這裡我們驗證服務端返回的name是不是我們傳過去的"sto".

然而這樣做是非常耗時的,如果只寫一兩個測試方法還好,對成千上百個方法這樣構建引數是非常繁瑣和乏味的,並且如果後端對資料有限制的情況下想構造出來符合條件的資料更是麻煩,並且後端介面如果欄位有修改則有可能造成測試失敗.由於測試專案引用了mvc專案,因此我們可以訪問到mvc專案裡的這些型別,然後使用AutoFixture動態建立這些型別的例項,然後序列化為Json提交,這樣會明顯減少工作量並增加程式的可維護性.

AutoFixture的安裝前面也提到過,並且它是支援.net core的,我們在Nuget包管理工具介面輸入AutoFixture.Xunit2進行搜尋,然後下載這個包即可,如果直接下載它則不用下載AutoFixture包,因為它是這個包的一個依賴,會自動安裝.

改造後的方法如下:

        [Theory]
        [AutoData]
        public async Task SimplePost(Student stud)
        {
            var content = new StringContent(JsonConvert.SerializeObject(stud), Encoding.UTF8, "application/json");
            var response = await _client.PostAsync("/HelloWorld/StudentInfo", content);
            response.EnsureSuccessStatusCode();
            var result =await response.Content.ReadAsStringAsync();
             Assert.True(!string.IsNullOrEmpty(result));
        }

首先fact註解變成了Theory註解,我們知道要為測試方法新增引數需要使用Theory註解,下面新增了AutoData註解,新增以後AutoFixture就會自動為方法的引數提供值.

下面我們把stud物件序列化為json字串,然後包裝成一個stringcontent物件提交到後臺,由於傳入的是什麼值是AutoFixture隨機建立的,我們並不知道,因此不能像上面一樣斷言它是"sto",但是它一定是有值的,因此我們斷言它不是null或者空字串.

然而遺憾的是,以上測試卻沒有通過,我們看一下錯誤皮膚資訊:

avatar
通過皮膚資訊我們看到AutoFixture構建的物件Name裡是有值的,然而卻返回的False,我們只知道結果,其中的過程我們並不清楚,也很難直觀的看到錯誤原因,這時候我們使用除錯模式來啟動測試程式

首先我們在測試方法剛進入的時候打上斷點,看看傳入的有沒有值
avatar

進入mvc專案,在方法剛進入的地方也打上斷點
avatar

下面我們對測試方法執行除錯

測試方法如何除錯在前面的章節中已經講過,我們在Test Explorer皮膚裡找到這個方法然後右鍵點選它的名字,在右鍵選單裡選擇"除錯單元測試",更為簡單的方法是,如果一個測試方法執行失敗,則在它的方法定義上會出現一個紅叉

avatar
我們點選這個紅叉就會出現一個浮動皮膚

avatar
下面有兩個按鈕一個是執行一個是除錯,點選除錯就可以進入除錯模式了.

第一個斷點處我們看到stud的每一個欄位都是有值的

avatar

按下F5繼續,進入mvc方法裡的斷點
avatar
我們看到這裡每個欄位繫結的都是預設值,也就是我們傳入的值並沒有繫結成功.

以上的除錯是為了展示如何除錯mvc測試專案,以上單步除錯並不能幫助我們太多,其實是因為.net core mvc改變了以往的繫結方式,改成了webapi 2.0的繫結方式,也就是要顯式的給引數加上FromBody註解方可成功繫結json資料

前面的示例我們看到formData是不需要註解就可以成功繫結的

我們改一下Mvc裡的方法,增加一個frombody註解

        [HttpPost]
        public IActionResult StudentInfo([FromBody]Student student)
        {
            return Content(student.Name);
        }

我們再次執行,就能夠正確繫結值了.

注意,很多人會想,是不是加了frombody註解以後即能夠接收formdata型別的引數,又能接收json型別引數呢,其實答案是否定的,如果加上了frombody後再提供formdata型別引數,則會返回415不支援的格式錯誤.因此這裡要權衡,如果是傳統的mvc專案通過form提交,則不能新增frombody,當然有一些外掛可以把form序列化為json,這樣就可以了.

有引起朋友可能會有疑惑,我直接從瀏覽器傳送請求或者使用postman工具就行了,幹嘛這麼費勁呢?這樣做的意義何在呢?其實前面也說到過,使用postman或者其它工具請求一方面不利於自動化測試(當然,postman是可以做到的)另一方面這樣做依賴於外部web伺服器,如果有多個環境在釋出更新的時候遺漏了某一個環境就會造成測試結果的不穩定,並且測試環境遷移了,測試專案也要跟著遷移,增加了維護成本.使用asp.net core自身的記憶體web伺服器功能則完全不依賴於外部web伺服器或者外部測試工具,測試時自動啟動web伺服器,測試完成自己銷燬,極大地方便了持續測試.

相關文章