Microsoft文檔(https://docs.microsoft.com/en-us/aspnet/core/testing/integration-testing)解釋瞭如何使用TestServer類實現集成測試。我們使用WEB API很容易,因爲我們將序列化模型作爲操作的響應。控制器操作的ASP.NET核心集成測試
但是,如果我想測試返回包含一些數據的HTML視圖的Controller操作,我該如何評估頁面內容是我期望的(避免掃描HTML頁面內容)?
Microsoft文檔(https://docs.microsoft.com/en-us/aspnet/core/testing/integration-testing)解釋瞭如何使用TestServer類實現集成測試。我們使用WEB API很容易,因爲我們將序列化模型作爲操作的響應。控制器操作的ASP.NET核心集成測試
但是,如果我想測試返回包含一些數據的HTML視圖的Controller操作,我該如何評估頁面內容是我期望的(避免掃描HTML頁面內容)?
一個選擇是使用類似硒使用自動化UI測試
一個你已經設置好你的WebHostBuilder以及TESTSERVER對象,你可以附加到你的HTML渲染頁面用於創建的JSON序列模型查看內容。例如:
<!DOCTYPE html>
<html>
<head></head>
<body>
<div>Subject</div><div>Kiss Me</div>
<div>Message</div><div>Ciao</div>
</body>
</html>
<script type="model/json">
{"Date":"2017-02-22","Subject":"Kiss Me","Result":true,"Message":"Ciao"}
</script>
然後在您的測試中,只需要創建一個通用的方法來反序列化模型:
public const string StartViewModelContainer = "<script type=\"model/json\">";
public const string EndViewModelContainer = "</script>";
protected virtual T GetModel<T>(string responseContent)
{
var result = default(T);
if (!string.IsNullOrWhiteSpace(responseContent))
{
var index = responseContent.IndexOf(StartViewModelContainer, 0, StringComparison.InvariantCulture);
if (index > 0)
{
var startingPosition = index + StartViewModelContainer.Length;
var endingPosition = responseContent.IndexOf(
EndViewModelContainer,
startingPosition,
StringComparison.InvariantCulture);
if (endingPosition <= startingPosition) return result;
var jSonModel = responseContent.Substring(startingPosition, endingPosition - startingPosition);
result = JsonConvert.DeserializeObject<T>(jSonModel);
}
}
return result;
}
後使用它來評估的結果。假設從視圖中使用的視圖模型是「GreetingViewModel」,只是做:
var greetingViewModel = GetModel<GreetingViewModel>(resonseContent);
Assert.AreEqual(greetingViewModel.Message, "Ciao");
Assert.AreEqual(greetingViewModel.Subject, "Kiss Me");
爲了這個JSON序列化的視圖模型添加到您的網頁,我實現了以下過濾器:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Newtonsoft.Json;
using Ticketino.Web.Components.Extensions.Request;
using Ticketino.Web.OnlineShop.Serializations;
using Ticketino.Web.OnlineShop.ViewModels.Base;
namespace Ticketino.Web.OnlineShop.Filters
{
/// <summary>
/// This is a filter used only for integration tests.
/// It format the ViewModel as jSon and appends it to the end of HMTL page, so that it can be deserialized from the test in order to check its values.
/// </summary>
/// <seealso cref="Microsoft.AspNetCore.Mvc.Filters.ResultFilterAttribute" />
[AttributeUsage(AttributeTargets.Method)]
public class IntegrationTestFilterAttribute : ResultFilterAttribute
{
public const string StartViewModelContainer = "<script type=\"model/json\">";
public const string EndViewModelContainer = "</script>";
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (!filterContext.ModelState.IsValid)
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult?.Model is BaseViewModel)
{
var errors = IntegrationTestFilterAttribute.GetModelErrors(filterContext.ModelState);
((BaseViewModel)viewResult.Model).ValidationErrors = errors;
}
}
base.OnResultExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult?.Model != null)
{
var jsonViewModel = string.Concat(
IntegrationTestFilterAttribute.StartViewModelContainer,
JsonConvert.SerializeObject(viewResult.Model, Formatting.None, CommonJsonSerializerSettings.Settings()),
IntegrationTestFilterAttribute.EndViewModelContainer);
filterContext.HttpContext.Response.WriteAsync(jsonViewModel);
}
}
base.OnResultExecuted(filterContext);
}
#region Private methods
private static IDictionary<string, string> GetModelErrors(ModelStateDictionary errDictionary)
{
var errors = new Dictionary<string, string>();
//get all entries from the ModelStateDictionary that have any errors and add them to our Dictionary
errDictionary.Where(k => k.Value.Errors.Count > 0).ForEach(i =>
{
foreach (var errorMessage in i.Value.Errors.Select(e => e.ErrorMessage))
{
errors.Add(i.Key, errorMessage);
}
});
return errors;
}
#endregion
}
}
然後,在ConfigureServices(IServiceCollection serviceCollection)方法中,在運行集成測試時將其注入,如下所示:
// Filter to append json serialized view model to buttom html response page, in order to eveluate from integration test class
if (_hostingEnvironment.IsIntegrationTest())
{
mvcBuilder.AddMvcOptions(opt => { opt.Filters.Add(new IntegrationTestFilterAttribute()); });
}