用于控制器操作的ASP.NET核心集成测试

时间:2017-02-22 09:07:55

标签: asp.net-core asp.net-core-mvc integration-testing

Microsoft文档(https://docs.microsoft.com/en-us/aspnet/core/testing/integration-testing)解释了如何使用TestServer类实现集成测试。我们使用WEB API很容易,因为我们将序列化模型作为动作的响应。

但是如果我想测试Controller操作返回包含一些数据的HTML视图,我如何评估页面内容是我期望的(避免扫描HTML页面内容)?

2 个答案:

答案 0 :(得分:0)

一种选择是使用Selenium

之类的东西来使用自动UI测试

答案 1 :(得分:0)

您已经设置了WebHostBuilder以及TestServer对象,您可以将用于创建View内容的JSON序列化模型附加到HTML呈现页面。例如:

<!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;
    }

之后用它来评估结果。假设View中使用的ViewModel是&#34; GreetingViewModel&#34;,只需执行:

            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)方法中,在运行集成测试时将其注入show:

        // 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()); });
        }