c# – 单元测试上载文件的Web API端点
发布时间:2020-12-16 01:27:16 所属栏目:百科 来源:网络整理
导读:我有一个web api终点,我想进行单元测试.我有一个自定义的SwaggerUploadFile属性,允许在swagger页面上的文件上传按钮.但是对于单元测试,我无法弄清楚如何传入文件. 对于单元测试,我使用:Xunit,Moq和Fluent Assertions 下面是我的控制器与端点: public class
我有一个web api终点,我想进行单元测试.我有一个自定义的SwaggerUploadFile属性,允许在swagger页面上的文件上传按钮.但是对于单元测试,我无法弄清楚如何传入文件.
对于单元测试,我使用:Xunit,Moq和Fluent Assertions 下面是我的控制器与端点: public class MyAppController : ApiController { private readonly IMyApp _myApp; public MyAppController(IMyApp myApp) { if (myApp == null) throw new ArgumentNullException(nameof(myApp)); _myApp = myApp; } [HttpPost] [ResponseType(typeof(string))] [Route("api/myApp/UploadFile")] [SwaggerUploadFile("myFile","Upload a .zip format file",Required = true,Type = "file")] public async Task<IHttpActionResult> UploadFile() { if (!Request.Content.IsMimeMultipartContent()) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } var provider = await Request.Content.ReadAsMultipartAsync(); var bytes = await provider.Contents.First().ReadAsByteArrayAsync(); try { var retVal = _myApp.CheckAndSaveByteStreamAsync(bytes).Result; if(retVal) { return ResponseMessage( new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonConvert.SerializeObject( new WebApiResponse { Message = "File has been saved" }),Encoding.UTF8,"application/json") }); } return ResponseMessage( new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(JsonConvert.SerializeObject( new WebApiResponse { Message = "The file could not be saved" }),"application/json") }); } catch (Exception e) { //log error return BadRequest("Oops...something went wrong"); } } } 我到目前为止的单元测试: [Fact] [Trait("Category","MyAppController")] public void UploadFileTestWorks() { //Arrange _myApp.Setup(x => x.CheckAndSaveByteStreamAsync(It.IsAny<byte[]>())).ReturnsAsync(() => true); var expected = JsonConvert.SerializeObject( new WebApiResponse { Message = "The file has been saved" }); var _sut = new MyAppController(_myApp.Object); //Act var retVal = _sut.UploadFile(); var content = (ResponseMessageResult)retVal.Result; var contentResult = content.Response.Content.ReadAsStringAsync().Result; //Assert contentResult.Should().Be(expected); } 如果(!Request.Content.IsMimeMultipartContent())我们得到NullReferenceException> “{“你调用的对象是空的.”}” 最佳答案已实施: 创建了一个界面: public interface IApiRequestProvider { Task<MultipartMemoryStreamProvider> ReadAsMultiPartAsync(); bool IsMimeMultiPartContent(); } 然后是一个实现: public class ApiRequestProvider : ApiController,IApiRequestProvider { public Task<MultipartMemoryStreamProvider> ReadAsMultiPartAsync() { return Request.Content.ReadAsMultipartAsync(); } public bool IsMimeMultiPartContent() { return Request.Content.IsMimeMultipartContent(); } } 现在我的控制器使用构造函数注入来获取RequestProvider: private readonly IMyApp _myApp; private readonly IApiRequestProvider _apiRequestProvider; public MyAppController(IMyApp myApp,IApiRequestProvider apiRequestProvider) { if (myApp == null) throw new ArgumentNullException(nameof(myApp)); _myApp = myApp; if (apiRequestProvider== null) throw new ArgumentNullException(nameof(apiRequestProvider)); _apiRequestProvider= apiRequestProvider; } 方法的新实现: [HttpPost] [ResponseType(typeof(string))] [Route("api/myApp/UploadFile")] [SwaggerUploadFile("myFile",Type = "file")] public async Task<IHttpActionResult> UploadFile() { if (!_apiRequestProvider.IsMimeMultiPartContent()) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } var provider = await _apiRequestProvider.ReadAsMultiPartAsync(); var bytes = await provider.Contents.First().ReadAsByteArrayAsync(); try { var retVal = _myApp.CheckAndSaveByteStreamAsync(bytes).Result; if(retVal) { return ResponseMessage( new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonConvert.SerializeObject( new WebApiResponse { Message = "File has been saved" }),"application/json") }); } catch (Exception e) { //log error return BadRequest("Oops...something went wrong"); } } } 我的单元测试嘲笑ApiController请求: [Fact] [Trait("Category","MyAppController")] public void UploadFileTestWorks() { //Arrange _apiRequestProvider = new Mock<IApiRequestProvider>(); _myApp = new Mock<IMyApp>(); MultipartMemoryStreamProvider fakeStream = new MultipartMemoryStreamProvider(); fakeStream.Contents.Add(CreateFakeMultiPartFormData()); _apiRequestProvider.Setup(x => x.IsMimeMultiPartContent()).Returns(true); _apiRequestProvider.Setup(x => x.ReadAsMultiPartAsync()).ReturnsAsync(()=>fakeStream); _myApp.Setup(x => x.CheckAndSaveByteStreamAsync(It.IsAny<byte[]>())).ReturnsAsync(() => true); var expected = JsonConvert.SerializeObject( new WebApiResponse { Message = "The file has been saved" }); var _sut = new MyAppController(_myApp.Object,_apiRequestProvider.Object); //Act var retVal = _sut.UploadFile(); var content = (ResponseMessageResult)retVal.Result; var contentResult = content.Response.Content.ReadAsStringAsync().Result; //Assert contentResult.Should().Be(expected); } 感谢@Badulake的想法 解决方法
你应该在方法的逻辑中做一个更好的分离.
重构您的方法,因此它不依赖于与您的Web框架相关的任何类,在本例中是Request类.您的上传代码无需了解任何相关信息. 作为提示: var provider = await Request.Content.ReadAsMultipartAsync(); 可以转换为: var provider = IProviderExtracter.Extract(); public interface IProviderExtracter { Task<provider> Extract(); } public class RequestProviderExtracter:IProviderExtracter { public Task<provider> Extract() { return Request.Content.ReadAsMultipartAsync(); } } 在您的测试中,您可以轻松地模拟IProviderExtracter,并专注于执行代码的每个部分. 我们的想法是获得最多的解耦代码,因此您的担忧只集中在模拟您开发的类,而不是框架强制您使用的类. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |