带有嵌套对象的集成测试表单

时间:2018-11-04 03:25:35

标签: .net-core integration-testing aspnetboilerplate

我正在将.NET Core与Boilerplate一起使用。我正在尝试对一些新表单进行单元测试,这些表单需要具有属性的嵌套对象。集成测试使用AbpAspNetCoreIntegratedTestBase<Startup>,它同时使用HttpClientTestServer的实例。客户可以使用各种方法。仅举几个例子,有GetAsync,PostAsync,SendSync和PutAsync方法。

我以为我已经熟悉了该框架中的一些方法和辅助方法,并且到目前为止已经取得了成功。但是,我有一个带有模型的表格,称为供应商,供应商有一个地址模型作为视图模型的一部分。因此,我可以将地址视图模型与应用程序中也需要地址的其他项一起重用。
与BoilerPlate一起使用的帮助者之一是GetUrl<TController>(string actionName, object queryStringParamsAsAnonymousObject),因为这是我尝试使用的表单public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)的帖子,无论我试图做什么,我都会得到400错误的请求响应,并且在进入控制器方法之前,我的测试失败。我不知道该如何处理。
这是我的模特:

VendorViewModel:

[AutoMap(typeof(Domains.Vendor))]
public class VendorViewModel : BaseViewModelEntity
{
    [Required]
    public string Name { get; set; }
    [Required]
    public string PointOfContact { get; set; }
    [Required]
    public string Email { get; set; }
    [Required]
    public int AddressId { get; set; }
    //[Required]
    //public string Address1 { get; set; }
    //public string Address2 { get; set; }
    //public string Address3 { get; set; }
    //[Required]
    //public string City { get; set; }
    //[Required]
    //public int State { get; set; }
    //[Required]
    //public string Zip { get; set; }
    //[Required]
    //public string Phone { get; set; }
    //public string Fax { get; set; }
    public AddressViewModel VendorAddress { get; set; }


    public VendorViewModel()
    {
        VendorAddress = new AddressViewModel();
    }

    public VendorViewModel(VendorDto vendor)
    {
        Id = vendor.Id;
        Name = vendor.Name;
        IsActive = vendor.IsActive;
        PointOfContact = vendor.PointOfContact;
        Email = vendor.Email;
        AddressId = vendor.AddressId;
        CreatorUserId = vendor.CreatorUserId;
        CreationTime = vendor.CreationTime;
        DeleterUserId = vendor.DeleterUserId;
        DeletionTime = vendor.DeletionTime;
        LastModificationTime = vendor.LastModificationTime;
        LastModifierUserId = vendor.LastModifierUserId;
        //Address1 = vendor.Address.Address1;
        //Address2 = vendor.Address.Address2;
        //Address3 = vendor.Address.Address3;
        //City = vendor.Address.City;
        //State = vendor.Address.State;
        //Zip = vendor.Address.Zip;
        //Phone = vendor.Address.Phone;
        //Fax = municipalities.Address.Fax;
       VendorAddress = new AddressViewModel()
        {
            Id = vendor.Address.Id,
            Address1 = vendor.Address.Address1,
            Address2 = vendor.Address.Address2,
            Address3 = vendor.Address.Address3,
            City = vendor.Address.City,
            State = vendor.Address.State,
            Zip = vendor.Address.Zip,
            Phone = vendor.Address.Phone,
            Fax = vendor.Address.Fax,
            CreationTime = vendor.Address.CreationTime,


        };


    }
}

地址视图模型:

public class AddressViewModel : BaseViewModelEntity
{
    [Required]
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string Address3 { get; set; }
    [Required]
    public string City { get; set; }
    [Required]
    public int State { get; set; }
    [Required]
    public string Zip { get; set; }
    [Required]
    public string Phone { get; set; }
    public string Fax { get; set; }

    public AddressViewModel()
    {

    }

    public AddressViewModel(AddressDto address)
    {
        Id = address.Id;
        Address1 = address.Address1;
        Address2 = address.Address2;
        Address3 = address.Address3;
        City = address.City;
        State = address.State;
        Zip = address.Zip;
        Phone = address.Phone;
        Fax = address.Phone;
        CreatorUserId = address.CreatorUserId;
        CreationTime = address.CreationTime;
        DeleterUserId = address.DeleterUserId;
        DeletionTime = address.DeletionTime;
        LastModificationTime = address.LastModificationTime;
        LastModifierUserId = address.LastModifierUserId;
   }
 }

我使用xUnit设置了测试

//Arrange
//Add Client Headers so User Auth and Permission Checkers work correctly
        Client.DefaultRequestHeaders.Add("my-name", "admin");
        Client.DefaultRequestHeaders.Add("my-id", "2");
//set up test data
        var addressViewModel = new AddressViewModel()
        {
            Address1 = "123 This Way", City = "Arlington", State = 44, Zip = "76001", Phone = "8175555555",
            CreationTime = DateTime.Now
        };

        var viewModelSave = new VenderViewModel()
        {
            Name = "Controller Test Name",
            PointOfContact = "Tom Jerry",
            Email = "Tom.Jerry@yolo.com",
            CreationTime = DateTime.Now,
            LastModificationTime = null,
            IsActive = true,
            AddressId = 0,
            VendorAddress = addressViewModel
            //Address1 = "123 This Way",
            //City = "Arlington",
            //State = 44,
            //Zip = "76001",
            //Phone = "8175555555"
        };
/* This is an attempt to use string interpolation to create querystring parameters */
        //var rawData =
        //    $"?Name={viewModelSave.Name}&Id={viewModelSave.Id}&PointOfContat=${viewModelSave.PointOfContact}&Email={viewModelSave.Email}&CreationTime={DateTime.Now}" +
        //    $"&LastModificationTime=&IsActive={viewModelSave.IsActive}&AddressId={viewModelSave.AddressId}&VendorAddress.Id={viewModelSave.VendorAddress.Id}&VendorAddress.Address1={viewModelSave.VendorAddress.Address1}" +
        //    $"&VendorAddress.City={viewModelSave.VendorAddress.City}&VendorAddress.State={viewModelSave.VendorAddress.State}&VendorAddress.Zip={viewModelSave.VendorAddress.Zip}&VendorAddress.Phone={viewModelSave.VendorAddress.Phone}" +
        //    $"&VendorAddress.CreationTime={DateTime.Now}&VendorAddress.IsActive={viewModelSave.VendorAddress.IsActive}";

/*This is an attempt to create a json object that could be serialize into an object as the "queryStringParamsAsAnonymousObject" that can be used in the GetUrl Helper method below */

        var rawData = $"{{'Name':'Controller Test Name','PointOfContact':'Tom Jerry', 'Email': 'Tom.Jerry@yolo.com',"
                       + "'CreationTime':'" +  DateTime.Now + "','LastModificationTime':'','IsActive' : 'true','AddressId':'0','Address.Address1':'123 This Way',"
                       + "'Address.City':'Arlington','Address.State':'44','Address.Zip':'76001','Address.Phone':'8175555556','Address.IsActive':'true'}";

        var jsonData = JsonConvert.DeserializeObject(rawData);

       //Serialize ViewModel to send with Post as part of the HttpContent object 
        var data = JsonConvert.SerializeObject(viewModelSave);


        var vendorAddress = new
        {
            viewModelSave.vendorAddress.Id,
            viewModelSave.vendorAddress.Address1,
            viewModelSave.vendorAddress.City,
            viewModelSave.vendorAddress.State,
            viewModelSave.vendorAddress.Zip,
            viewModelSave.vendorAddress.Phone,
            viewModelSave.vendorAddress.IsActive,
            viewModelSave.vendorAddress.CreationTime
        };

        //actually get the url from helper method (with various attempts at creating an anonymousObject directly
        var url = GetUrl<VendorController>(nameof(VendorController.SaveVendor),
    new
    {
        viewModelSave.Id,
        viewModelSave.Name,
        viewModelSave.PointOfContact,
        viewModelSave.Email,
        viewModelSave.CreationTime,
        viewModelSave.LastModificationTime,
        viewModelSave.IsActive,
        viewModelSave.AddressId,
        vendorAddress
        //VendorAddress_Address1 = vendorAddress.Address1,
        //VendorAddress_Id = vendorAddress.Id,
        //VendorAddress_City = vendorAddress.City,
        //VendorAddress_State = vendorAddress.State,
        //VendorAddress_Zip = vendorAddress.Zip,
        //VendorAddress_Phone = vendorAddress.Phone,
        //VendorAddress_IsActive = vendorAddress.IsActive,
        //VendorAddress = new 
        //{
        //    viewModelSave.VendorAddress.Id,
        //    viewModelSave.VendorAddress.Address1,
        //    viewModelSave.VendorAddress.City,
        //    viewModelSave.VendorAddress.State,
        //    viewModelSave.VendorAddress.Zip,
        //    viewModelSave.VendorAddress.Phone,
        //    viewModelSave.VendorAddress.IsActive,
        //    viewModelSave.VendorAddress.CreationTime
        //},
        //viewModelSave.Address1,
        //viewModelSave.City,
        //viewModelSave.State,
        //viewModelSave.Zip,
        //viewModelSave.Phone
    }

    );
        var content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded");
        var message = new HttpRequestMessage {
            Content = content,
            Method = HttpMethod.Post,
            RequestUri = new Uri("http://localHost" + url)

            };

        //Act
        var response = await PostResponseAsObjectAsync<AjaxResponse>(url, content);

        //Assert
        var count = UsingDbContext(context => { return context.Municipalities.Count(x => x.IsActive); });

        response.ShouldBeOfType<AjaxResponse>();
        response.Result.ShouldNotBeNull();
        count.ShouldBe(3);
    }

我尝试调试正在发生的事情。我注意到,通过测试请求发送的VendorAddress属性与在Chrome开发人员工具中进行解析时,实际的表单发布看起来不匹配。在Chrome浏览器中,我看到了(示例: 联系人:“汤姆·杰里” IsActive:真 VendorAddress.Address1:“ 123这样” VendorAddress.City:“阿灵顿”) 我无法将测试数据转换为相同的格式,因此在发布后无法正确绑定到我的视图模型,因此返回400响应并导致测试失败。

如果我一起删除了地址视图模型并将这些属性作为VendorViewModel的属性,我就可以使用它。但是,如果我尝试将对象集合与主视图模型一起保存,则会遇到相同甚至相同的问题。 我觉得必须有一种通过与样板的集成测试提交测试表单数据的方法。我只需要这个拼图缺少一些东西即可。

1 个答案:

答案 0 :(得分:0)

适用于发布和发送请求的解决方案: .NET Core有一个QueryHelper类,该类将使用字典和uri字符串并将其转换为带有querystring参数的url。 QueryHelpers.AddQueryString(string uri,IDictionary queryString)这是Microsoft.AspNetCore.WebUtilities的一部分。 使用此方法,我可以正确准备formData。对于发帖请求,测试看起来像这样

//Arrange
//Add Client Headers so User Auth and Permission Checkers work correctly
Client.DefaultRequestHeaders.Add("my-name", "admin");
Client.DefaultRequestHeaders.Add("my-id", "2");
//set up test data

var rawData = new Dictionary<string, string>(){{ "Id", "0"}, {"Name", "Controller Test Name"}, {"PointOfContact", "Tom Jerry"}
            , {"Email", "Tom.Jerry@yolo.comy"}, {"CreationTime", $"{DateTime.Now}"}, {"LastModificationTime", ""}, {"IsActive", "true"}, {"AddressId", "0"}
            , {"VendorAddress.Id", "0"}, {"VendorAddress.Address1", "123 This Way"}, {"VendorAddress.City", "Arlington"}, {"VendorAddress.State", "44"}, {"VendorAddress.Zip", "76001"}
            , {"VendorAddress.Phone", "8175555555"}, {"VendorAddress.Fax", ""}, {"VendorAddress.IsActive", "true"}, {"VendorAddress.CreationTime", $"{DateTime.Now}"}, {"VendorAddress.LastModificationTime", ""}
        };
var jsonData = JsonConvert.DeserializeObject(rawData);

//Serialize ViewModel to send with Post as part of the HttpContent object 
var data = JsonConvert.SerializeObject(viewModelSave);

//actually get the url from helper method 
var url = GetUrl<VendorController>(nameof(VendorController.SaveVendor));

var content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded");

//Act
var response = await PostResponseAsObjectAsync<AjaxResponse>(url, content);

//Assert
var count = UsingDbContext(context => { return context.Municipalities.Count(x => x.IsActive); });

response.ShouldBeOfType<AjaxResponse>();
response.Result.ShouldNotBeNull();
count.ShouldBe(3);

发送请求测试看起来像这样

//Arrange
//Add Client Headers so User Auth and Permission Checkers work correctly
Client.DefaultRequestHeaders.Add("my-name", "admin");
Client.DefaultRequestHeaders.Add("my-id", "2");
//set up test data

var rawData = new Dictionary<string, string>(){{ "Id", "0"}, {"Name", "Controller Test Name"}, {"PointOfContact", "Tom Jerry"}
            , {"Email", "Tom.Jerry@yolo.comy"}, {"CreationTime", $"{DateTime.Now}"}, {"LastModificationTime", ""}, {"IsActive", "true"}, {"AddressId", "0"}
            , {"VendorAddress.Id", "0"}, {"VendorAddress.Address1", "123 This Way"}, {"VendorAddress.City", "Arlington"}, {"VendorAddress.State", "44"}, {"VendorAddress.Zip", "76001"}
            , {"VendorAddress.Phone", "8175555555"}, {"VendorAddress.Fax", ""}, {"VendorAddress.IsActive", "true"}, {"VendorAddress.CreationTime", $"{DateTime.Now}"}, {"VendorAddress.LastModificationTime", ""}
        };
var jsonData = JsonConvert.DeserializeObject(rawData);

//Serialize ViewModel to send with Post as part of the HttpContent object 
var data = JsonConvert.SerializeObject(viewModelSave);

//actually get the url from helper method 
var url = GetUrl<VendorController>(nameof(VendorController.SaveVendor));

var content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded");
var message = new HttpRequestMessage {
    Content = content,
    Method = HttpMethod.Post,
    RequestUri = new Uri("http://localHost" + url)
};

//Act
var response = await SendResponseAsObjectAsync<AjaxResponse>(message);

//Assert
var count = UsingDbContext(context => { return context.Municipalities.Count(x => x.IsActive); });

response.ShouldBeOfType<AjaxResponse>();
response.Result.ShouldNotBeNull();
count.ShouldBe(3);