I'm trying to learn webapi and have stumbled across a problem. The training course I was doing showed how to do paging by returning a response header with the next and previous link. However it uses HttpContext.Current.Response.Headers.Add() to send back the next link, previous link, and total pages.
I am also trying to implement unit tests for the controllers. Problem seems to be that the HttpContext.Current is null when running through a unit test. I read somewhere that I shouldn't be HttpContext.Current for webapi as it's not testable, but I'm not sure what I should be using instead.
Here is my contoller code:
public partial class CandidateManagerController
{
private readonly ICandidateManager _candidateManagerV2;
public CandidateManagerController(ICandidateManager candidateManager)
{
_candidateManagerV2 = candidateManager;
}
[VersionedRoute("CandidateManager", 2, Name="CandidateManagerV2")]
public IHttpActionResult Get(int page = 1, int pageSize = 1)
{
try
{
var totalCount = 0;
var totalPages = 0;
var result = _candidateManagerV2.GetCandidates(out totalCount, out totalPages, page, pageSize);
var urlHelper = new UrlHelper(Request);
var prevLink = page > 1
? urlHelper.Link("CandidateManagerV2",
new
{
page = page - 1,
pageSize = pageSize,
})
: "";
var nextLink = page < totalPages ? urlHelper.Link("CandidateManagerV2",
new
{
page = page + 1,
pageSize = pageSize
}) : "";
var paginationHeader = new
{
currentPage = page,
pageSize = pageSize,
totalCount = totalCount,
totalPages = totalPages,
previousPageLink = prevLink,
nextPageLink = nextLink
};
HttpContext.Current.Response.Headers.Add("X-Pagination", Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader));
return Ok(result);
}
catch (Exception exp)
{
return InternalServerError();
}
}
}
Here is my unit test. Please note I'm using Nunit and Moq:
[TestFixture]
public class CandidateManagerControllerV2Tests
{
[Test]
[Category("CandidateManagerController Unit Tests")]
public void Should_Return_List_Of_Candidate_Objects()
{
var testList = new List<Candidate>();
testList.Add(new Candidate() { CandidateId = 1, Name = "Mr", Surname = "Flibble" });
testList.Add(new Candidate() { CandidateId = 2, Name = "Arnold", Surname = "Rimmer" });
var totalCount = 0;
var totalPages = 0;
var mockManager = new Mock<ICandidateManager>();
mockManager.Setup(x => x.GetCandidates(out totalCount, out totalPages, It.IsAny<int>(), It.IsAny<int>())).Returns(testList);
var controller = new CandidateManagerController(mockManager.Object);
SetupControllerForTests(controller);
var result = controller.Get(1, 1);
}
private static void SetupControllerForTests(ApiController controller)
{
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/candidatemanager");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
controller.ActionContext=new HttpActionContext();
}
}
I'm hoping someone will be able to help me. It could be that I've been led down a wrong path with the way to implement paging. However it is likely that I'd need to add a response header for something any way.
You should avoid coupling yourself to HttpContext.
Here is another approach to how you can set the header and still be able to unit test it as you intended. You create a HttpResponseMessage, add headers as needed and then create a ResponseMessageResult from it:
//...code removed for brevity
var response = Request.CreateResponse(HttpStatusCode.OK, result);
response.Headers.Add("X-Pagination", Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader));
IHttpActionResult ok = ResponseMessage(response);
return ok;
You should also note that your controller setup will cause a null reference error when creating your UrlHelper because you are resetting the controller's Request to null when you assign the default ActionContext
private static void SetupControllerForTests(ApiController controller) {
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/candidatemanager");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
//commented this out as it was causing Request to be null
//controller.ActionContext=new HttpActionContext();
}
The following test passed when checking for the X-Pagination header
public async Task Should_Return_Paged_List_Of_Candidate_Objects() {
//Arrange
var testList = new List<Candidate>();
testList.Add(new Candidate() { CandidateId = 1, Name = "Mr", Surname = "Flibble" });
testList.Add(new Candidate() { CandidateId = 2, Name = "Arnold", Surname = "Rimmer" });
var totalCount = 0;
var totalPages = 0;
var mockManager = new Mock<ICandidateManager>();
mockManager.Setup(x => x.GetCandidates(out totalCount, out totalPages, It.IsAny<int>(), It.IsAny<int>())).Returns(testList);
var controller = new CandidateManagerController(mockManager.Object);
SetupControllerForTests(controller);
//Act
var response = await controller.Get(1, 1).ExecuteAsync(System.Threading.CancellationToken.None);
//Assert
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(HttpResponseMessage));
Assert.IsTrue(response.Headers.Contains("X-Pagination"));
}
To test your response headers you need to do the following:
Initialize your controller with a ControllerContext in TestInitialize
Call the controller where you add the custom header
Assert it in the TestMethod
It only works if you add your header in the controller class: HttpContext.Response.Headers.Add("x-custom-header", "value");
Example:
public class MyControllerTests
{
private MyController _controller;
[TestInitialize]
public void Setup()
{
_controller= new MyController();
_controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext(),
};
}
[TestMethod]
public async Task GetAsyncShouldContainCutomHeader()
{
// Act
await _controller.GetAsync().ConfigureAwait(false);
// Assert
Assert.IsTrue(_controller.Response.Headers.ContainsKey("x-custom-header"));
Assert.IsTrue(_controller.Response.Headers["x-custom-header"].Equals("value"));
}
}
Related
I'm new at asp.net and moq and xunit test, I want to create a test case for my asp.net project. I have the service file that is CRUD action,
I have been written for Create Method it's Ok but at Get, GetAll, Delete, Put method I don't know what should I setup
public void Test1()
{
var tutorRequest = new Mock<ITutorRequestService>();
//What should i set up for Get and GetAll
tutorRequest.Setup(r => r.Get(It.IsAny<int>(), It.IsAny<string?>()))
.ReturnsAsync(() => new Entity());
var logger = new Mock<ILogger<TutorRequestsController>>();
var controller = new TutorRequestsController(tutorRequest.Object, logger.Object);
var rs = controller.Get(1, "");
Assert.NotNull(rs);
}
My service class
public async Task<TutorRequest> Create(TutorRequestModel tutor)
{
var entity = _mapper.Map<TutorRequest>(tutor);
CheckDataNotNull("entity", entity);
entity = await _repository.Add(entity);
return _mapper.Map<TutorRequest>(entity);
}
public async Task<TutorRequest> Delete(int tutorId)
{
var entity = await _repository.Delete(tutorId);
return _mapper.Map<TutorRequest>(entity);
}
public async Task<Entity> Get(int id, string? fields)
{
var entity = await _repository.Get(id);
CheckDataNotNull("tutor request", entity);
var shaped = _dataShaper.ShapeData(entity, fields);
CheckDataNotNull("shaped", shaped);
return _mapper.Map<Entity>(shaped);
}
public PageList<Entity> GetAll(TutorRequestParams param)
{
var listAll = _repository.GetAll();
CheckDataNotNull("shaped", listAll);
Search(ref listAll, param);
var sortedStudent = _sortHelper.ApplySort(listAll, param.OrderBy);
var shapedOwners = _dataShaper.ShapeData(sortedStudent, param.Fields);
return PageList<Entity>.ToPageList(shapedOwners, param.PageNume, param.PageSize);
}
public async Task<TutorRequest> Update(TutorRequestModel tutor)
{
var entity = _mapper.Map<TutorRequest>(tutor);
entity = await _repository.Update(entity);
return _mapper.Map<TutorRequest>(entity);
}
If you're expecting the GetAll method to return something, you need to set up the call for the mocked service.
mockRepo.Setup(r => r.GetAll(It.IsAny<TutorRequestParams>())).Returns(
new PagedList<ExpandoObject>
{
// result you're expecting
}
);
I have write a test case for Get method that similar to your example, you can check it out
public void Test_Get_Method()
{
var questService = new Mock<IQuestService>();
questService.Setup(x => x.Get(It.IsAny<Guid>()))
.ReturnsAsync(new QuestResponseModel()
{
Id = Guid.Empty,
Title = "Test",
Description = "TestDescription",
Price = 100,
Status = "Pending",
AvailableTime = DateTime.Now,
EstimatedTime = "120",
QuestTypeId = 1
});
var mapperMock = new Mock<IMapper>();
var controller = new QuestController(questService.Object);
var result = controller.Get(Guid.Empty);
Assert.Equal("TestDescription", result.Result.Data.Description);
}
Test Setup
Mock gives null object when the test hits the method inside the controller
My controller's constructor has two dependencies which I mocked. While running the test for CheckOut it is giving null object in the result. I am using AutoMapper to map between Model objects and Business
[Fact]
public void CheckOutSomethingsTest()
{
Mock<ICheckoutService> checkoutServiceMock = new Mock<ICheckoutService>();
Mock<IMapper> mapperMock = new Mock<IMapper>();
checkoutServiceMock.Setup(c => c.CheckoutSomethings(new CheckOutSomethingsInput
{
SecretKey = "SecureKey",
UserId = 100,
SomethingIds = new List<int> { 10001, 1002, 1003 }
}))
.Returns<List<CheckedOutSomething>>((o) => new List<CheckedOutSomething>
{
new CheckedOutSomething { Id = 10001, Remarks = "Success" },
new CheckedOutSomething { Id = 10002, Remarks = "Success" }
});
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddSomething(new AutoMapperBusinessToEntitiesSomething());
cfg.AddSomething(new AutoMapperModelsToBusinessSomething());
});
var mapper = new Mapper(configuration);
var controller = new SomethingsController(checkoutServiceMock.Object, mapper);
var result = controller.CheckOut(
new CheckOutSomethingInputModel
{
UserId = 100,
SomethingIds = new List<int> { 10001, 10002, 10003 }
});
Assert.IsType<OkObjectResult>(result.Result);
Assert.Equal(2, result.Value.Count);
}
Service
Constructor
public ProfilesController(ICheckoutService checkoutService, IMapper mapper)
{
this.checkoutService = checkoutService;
this.mapper = mapper;
}
Method
[HttpPost]
[Route("checkout")]
public ActionResult<List<CheckedOutSomethingModel>>
CheckOut([FromBody] CheckOutSomethingInputModel checkoutInput)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var input = mapper.Map<CheckOutSomethingsInput>(checkoutInput);
var output = mapper.Map<List<CheckedOutSomethingModel>>
(checkoutService.CheckoutSomethings(input));
return Ok(output);
}
I cannot figure out what am I doing wrong
#Fabio Many Thanks Is it like this
checkoutServiceMock.Setup(c => c.CheckoutSomethings(It.IsAny<CheckOutSomethingsInput>()))
.Returns(new List<CheckedOutSomething>
{
new CheckedOutSomething { Id = 10001, Remarks = "Success" },
new CheckedOutSomething { Id = 10002, Remarks = "Success" }
});
I actually did and It worked, Thanks Again
I am trying to unit test a c# controller method. I have looked up many questions and nothing quite gives me instructions on how to unit test the API controller. The code that is correct is below for the method. How do you unit test for the brackets.
I have figured out how to test the method "Reverse Payment", but I am using moq and xunit to test the actual c# method. This is my current unit test:
public class PaymentsControllerTests : BackOfficeIntegrationTestBase
{
private readonly CurrentDateProvider _currentDateProvider;
[BackOfficeRolesAuthorize(BackOfficeUserRole.Admin, BackOfficeUserRole.CSR, BackOfficeUserRole.DealerSupportRep,
BackOfficeUserRole.CSRManager)]
[Fact]
public async Task AdminReversalPermissions()
{
IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());
var mediator = fixture.Freeze<Mock<IMediator>>();
var currentDateProvider = fixture.Freeze<CurrentDateProvider>();
var mockRepo = new Mock<IMediator>();
var controller = new PaymentsController(mockRepo.Object);
// Setup Dependencies
//DataContext dataContext = SetUpDatabase(fixture);
ReversePaymentSetup(fixture, mediator);
var result = await controller.ReversePayment(LeaseInfo.ApplicationId, 100);
var viewResult = result as NoContentResult;
Assert.NotNull(viewResult);
}
private void ReversePaymentSetup(IFixture fixture, Mock<IMediator> mediator)
{
PaymentData data = new PaymentData();
//Mock the mediator
var paymentPlan = new Data.Model.PaymentPlan()
{
LeaseId = 1,
LeaseTerm = LeaseTerm.Year,
DefermentDays = 5,
FirstPaymentDate = DateTime.ParseExact("01/01/2019", "d", CultureInfo.InvariantCulture),
StoreState = "VA"
};
var responseData = new BuildPaymentPlanResponse();
responseData.Data = new BuildPaymentPlanResponse.InitializePaymentPlanResponseData()
{
PaymentPlan = paymentPlan
};
PaymentStatusChangeManualRequest request = fixture.Build<PaymentStatusChangeManualRequest>()
.With(x => x.PaymentPlanId, LeaseInfo.ApplicationId)
.With(x => x.PaymentId, 100)
.With(x => x.Status, PaymentStatus.Reversed)
.Create();
Assert.Equal(PaymentStatus.Reversed, request.Status);
Assert.Equal(100, request.PaymentId);
Assert.NotNull(LeaseInfo.ApplicationId);
}
}
}
[ProducesResponseType(typeof(ErrorResponse), (int)HttpStatusCode.NotFound)]
[HttpPut("{paymentId}/ReversePayment")]
[BackOfficeRolesAuthorize(BackOfficeUserRole.Admin, BackOfficeUserRole.CSR, BackOfficeUserRole.DealerSupportRep,
BackOfficeUserRole.CSRManager)]
public async Task<IActionResult> ReversePayment(int paymentPlanId, int paymentId)
{
await _mediator.Send(new PaymentStatusChangeManualRequest
{
PaymentPlanId = paymentPlanId,
PaymentId = paymentId,
Status = PaymentStatus.Reversed
});
return NoContent();
}
I'm writing some tests for my WebAPI web service and cannot figure out how to send JSON to my service method in the test.
ScheduleRequest sr = new ScheduleRequest();
sr.Months = null;
sr.States = null;
sr.Zip = null;
sr.Miles = null;
sr.PCodes = null;
sr.PageStart = 1;
sr.PageLimit = 10;
HttpRequestMessage m = new HttpRequestMessage();
string sr_ = JsonConvert.SerializeObject(sr);
// How do I load it into the HttpRequestMessage???
// m.Content. = sr_;
var controller = new ShoppingCartController();
// Call the controlelr method and test if the return data is correct.
EventSyncResponse res = (EventSyncResponse)controller.CourseSchedule(m);
Am I doing this correctly, too?
Controller Code:
public object CourseSchedule(ScheduleRequest request)
{
try
{
var result = cart.GetCourseSchedule(request);
return Ok(result);
}
catch (Exception ex)
{
if (ex.Message.StartsWith(#"ORA-20001"))
{
return Ok(new ParticipantResponse { FirstName = "No record found" });
}
throw ex;
}
}
[TestClass]
public class ShoppingCartControllerTests {
[TestMethod]
public void TestCourseSchedule() {
//Arrange
var sr = new ScheduleRequest();
sr.Months = null;
sr.States = null;
sr.Zip = null;
sr.Miles = null;
sr.PCodes = null;
sr.PageStart = 1;
sr.PageLimit = 10;
var json = JsonConvert.SerializeObject(sr);
//construct content to send
var content = new System.Net.Http.StringContent(json, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage {
RequestUri = new Uri("http://localhost/api/shoppingcart"),
Content = content
};
var controller = new ShoppingCartController();
//Set a fake request. If your controller creates responses you will need this
controller.Request = request;
//Act
// Call the controller method and test if the return data is correct.
var response = controller.CourseSchedule(request) as OkNegotiatedContentResult<List<EventSyncResponse>> ;
//Assert
//...other asserts
}
}
But I get the impression that your Action should actually be refactored like this in your controller
public class ShoppingCartController : ApiController {
public IHttpActionResult CourseSchedule(ScheduleRequest model) { ... }
}
which would mean that your isolated unit test should be refactored to...
[TestClass]
public class ShoppingCartControllerTests {
[TestMethod]
public void TestCourseSchedule() {
//Arrange
var sr = new ScheduleRequest();
sr.Months = null;
sr.States = null;
sr.Zip = null;
sr.Miles = null;
sr.PCodes = null;
sr.PageStart = 1;
sr.PageLimit = 10;
var controller = new ShoppingCartController();
//Set a fake request. If your controller creates responses you will need this
controller.Request = new HttpRequestMessage {
RequestUri = new Uri("http://localhost/api/shoppingcart"),
};
//Act
// Call the controller method and test if the return data is correct.
var response = controller.CourseSchedule(sr) as OkNegotiatedContentResult<List<EventSyncResponse>> ;;
//Assert
//...
}
}
MB34.
You need to add in your method, a ScheduleRequest parameter too.
Check this link:
http://www.lybecker.com/blog/2013/06/26/accessing-http-request-from-asp-net-web-api/
I have a web api end point that i want to unit test. I have a custom SwaggerUploadFile attribute that allows a file upload button on the swagger page. But for unit testing I cant figure out how to pass in a file.
For unit testing I am using: Xunit, Moq and Fluent Assertions
Below is my controller with the endpoint:
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"
}), Encoding.UTF8, "application/json")
});
}
catch (Exception e)
{
//log error
return BadRequest("Oops...something went wrong");
}
}
}
Unit test I have so far:
[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);
}
The above fails as when it hits this line if(!Request.Content.IsMimeMultipartContent()) we get a NullReferenceException > "{"Object reference not set to an instance of an object."}"
Best Answer Implemented:
Created an interface:
public interface IApiRequestProvider
{
Task<MultipartMemoryStreamProvider> ReadAsMultiPartAsync();
bool IsMimeMultiPartContent();
}
Then an implementation:
public class ApiRequestProvider : ApiController, IApiRequestProvider
{
public Task<MultipartMemoryStreamProvider> ReadAsMultiPartAsync()
{
return Request.Content.ReadAsMultipartAsync();
}
public bool IsMimeMultiPartContent()
{
return Request.Content.IsMimeMultipartContent();
}
}
Now my controller uses constructor injection to get the 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;
}
New implementation on method:
[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 (!_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"
}), Encoding.UTF8, "application/json")
});
}
return ResponseMessage(
new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(JsonConvert.SerializeObject(
new WebApiResponse
{
Message = "The file could not be saved"
}), Encoding.UTF8, "application/json")
});
}
catch (Exception e)
{
//log error
return BadRequest("Oops...something went wrong");
}
}
}
And my unit test that mocks the ApiController Request:
[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);
}
Thanks to #Badulake for the idea
You should do a better separation in the logic of the method.
Refactor your method so it does not depends on any class related to your web framework , in this case the Request class. Your upload code does not need to know anything about it.
As a hint:
var provider = await Request.Content.ReadAsMultipartAsync();
could be transformed to:
var provider = IProviderExtracter.Extract();
public interface IProviderExtracter
{
Task<provider> Extract();
}
public class RequestProviderExtracter:IProviderExtracter
{
public Task<provider> Extract()
{
return Request.Content.ReadAsMultipartAsync();
}
}
In your tests you can easely mock IProviderExtracter and focus in which work is doing each part of your code.
The idea is you get the most decoupled code so your worries are focused only in mocking the classes you have developed, no the ones the framework forces you to use.
The below was how I initially solved it but after Badulake's answer i implemented that where i abstracted the api request to an interface/class and Mocked it out with Moq. I edited my question and put the best implementation there, but i left this answer here for people who dont want to go to the trouble of mocking it
I used part of this guide but I made a simpler solution:
New unit test:
[Fact]
[Trait("Category", "MyAppController")]
public void UploadFileTestWorks()
{
//Arrange
var multiPartContent = CreateFakeMultiPartFormData();
_myApp.Setup(x => x.CheckAndSaveByteStreamAsync(It.IsAny<byte[]>())).ReturnsAsync(() => true);
var expected = JsonConvert.SerializeObject(
new WebApiResponse
{
Message = "The file has been saved"
});
_sut = new MyAppController(_myApp.Object);
//Sets a controller request message content to
_sut.Request = new HttpRequestMessage()
{
Method = HttpMethod.Post,
Content = multiPartContent
};
//Act
var retVal = _sut.UploadFile();
var content = (ResponseMessageResult)retVal.Result;
var contentResult = content.Response.Content.ReadAsStringAsync().Result;
//Assert
contentResult.Should().Be(expected);
}
Private support method:
private static MultipartFormDataContent CreateFakeMultiPartFormData()
{
byte[] data = { 1, 2, 3, 4, 5 };
ByteArrayContent byteContent = new ByteArrayContent(data);
StringContent stringContent = new StringContent(
"blah blah",
System.Text.Encoding.UTF8);
MultipartFormDataContent multipartContent = new MultipartFormDataContent { byteContent, stringContent };
return multipartContent;
}