我正在将.NET Core与Boilerplate一起使用。我正在尝试对一些新表单进行单元测试,这些表单需要具有属性的嵌套对象。集成测试使用AbpAspNetCoreIntegratedTestBase<Startup>
,它同时使用HttpClient
和TestServer
的实例。客户可以使用各种方法。仅举几个例子,有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的属性,我就可以使用它。但是,如果我尝试将对象集合与主视图模型一起保存,则会遇到相同甚至相同的问题。 我觉得必须有一种通过与样板的集成测试提交测试表单数据的方法。我只需要这个拼图缺少一些东西即可。
答案 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);