Current Solution
So I have something very similar to
[HttpPost]
public ActionResult Upload()
{
var length = Request.ContentLength;
var bytes = new byte[length];
if (Request.Files != null )
{
if (Request.Files.Count > 0)
{
var successJson1 = new {success = true};
return Json(successJson1, "text/html");
}
}
...
return Json(successJson2,"text/html");
}
Unit testable solution?
I want something like this:
[HttpPost]
public ActionResult Upload(HttpRequestBase request)
{
var length = request.ContentLength;
var bytes = new byte[length];
if (request.Files != null )
{
if (request.Files.Count > 0)
{
var successJson1 = new {success = true};
return Json(successJson1);
}
}
return Json(failJson1);
}
However this fails, which is annoying as I could make a Mock from the base class and use it.
Notes
I am aware this is not a good way to parse a form/upload and would
like to say other things are going on here (namely that this upload
can be a form or an xmlhttprequest - the action does not know which).
Other ways to make "Request" unit testable would also be awesome.
You already have a Request property on your controller => you don't need to pass it as action argument.
[HttpPost]
public ActionResult Upload()
{
var length = Request.ContentLength;
var bytes = new byte[length];
if (Request.Files != null)
{
if (Request.Files.Count > 0)
{
var successJson1 = new { success = true };
return Json(successJson1);
}
}
return Json(failJson1);
}
Now you can mock the Request in your unit test and more specifically the HttpContext which has a Request property:
// arrange
var sut = new SomeController();
HttpContextBase httpContextMock = ... mock the HttpContext and more specifically the Request property which is used in the controller action
ControllerContext controllerContext = new ControllerContext(httpContextMock, new RouteData(), sut);
sut.ControllerContext = controllerContext;
// act
var actual = sut.Upload();
// assert
... assert that actual is JsonResult and that it contains the expected Data
Related
I am currently running unit tests on a controller that utilizes Url.Action to get the absolute path of two different routes. I am using Moq to set up the mock Action. The test is currently failing with the message 'Object reference not set to an instance of an object.'
[Edit - Clarifying the question]
When I debug the test, the Url shows to as a Mock<IUrlHelper> with an ActionContext property, but on the line where it calls the Url.Action, the Action property shows as null. What am I missing when setting up the Mock<IUrlHelper> that way the Url.Action doesn't come back as null?
I've checked multiple options for setting up Mock Url Actions, but nothing has seemed to work for me.
Here is the setup for the test
[Fact]
public async void SendGroup_PassesDetailsToNotificationService()
{
UrlActionContext actualContext = null;
var criteria = new NotificationCriteria { Section = 1, Subsection = 2 };
userSvc.GetNotificationGroupReturnValue = new List<NotificationRecipient>
{
new NotificationRecipient{ SmsAuth = true }
};
var actionContext = new ActionContext
{
ActionDescriptor = new ActionDescriptor(),
RouteData = new RouteData(),
};
var urlHelper = new Mock<IUrlHelper>();
urlHelper.SetupGet(h => h.ActionContext).Returns(actionContext);
urlHelper.Setup(h => h.Action(It.IsAny<UrlActionContext>()))
.Callback((UrlActionContext context) => actualContext = context);
controller.Url = urlHelper.Object;
var dummy = await controller.SendGroup(criteria);
Assert.Same(criteria, notificationSvc.SendGroupNotificationDetailUsed);
}
This is the code for the controller
[HttpPost]
public async Task<IActionResult> SendGroup([FromBody]NotificationCriteria criteria)
{
List<NotificationRecipient> recipients = (await userSvc.GetNotificationGroup(
criteria.Section, criteria.Subsection)).ToList();
if (!recipients.Any())
{
return BadRequest();
}
var uro = Url;
var sectionUrl = Url.Action("Index", "ReportHome", new {sectionId = criteria.Section}, Request.Scheme);
var profileUrl = Url.Action("Index", "Profile", null, Request.Scheme);
var detail = new NotificationDetail
{
SectionUrl = sectionUrl,
ProfileUrl = profileUrl,
Recipients = recipients
};
try
{
await notificationSvc.SendGroupNotification(detail);
}
catch
{
return StatusCode(500);
}
return Ok();
}
I try to get result of GetAll() function in unit test, but i can't convert this to list. How can i do it correctly ?
Test:
[Fact]
public async Task GetAllHeroes_ShouldReturnAllHeroes()
{
var controller = new HeroesController(_heroes);
var response = await controller.GetHeroes() as List<Hero>;
//here i need response list, but there's error
}
Controller:
// GET: api/v1/heroes
[HttpGet]
[Produces(typeof(List<Hero>))]
public async Task<ActionResult<IEnumerable<Hero>>> GetHeroes()
{
var result = await _heroes.GetAll();
return Ok(result);
}
You need to parse the response to a list of your model
like this
var response = await controller.GetHeroes();
Assert.IsType<OkObjectResult>(result);
var content = ((OkObjectResult)result).Value;
Assert.IsType<List<Hero>>(content);
var Heros = (List<Hero>)content;
I added some other asserts that may be useful
Does something like this work:
// Act
var result = await controller.GetHeroes();
var response = result as OkNegotiatedContentResult<IEnumerable<Hero>>;
// Assert
// Assert.IsNotNull(response);
var heroes = response.Content.Result;
Assert.IsInstanceOfType(heroes, typeof(IEnumerable<Hero>), "Incorrect Types");
var dtos = heroes.ToArray();
// Asserting output
Assert.AreEqual(1, dtos.Length);
Assert.AreEqual(expected[0].id, dtos[0].id);
For get IEnumerable, that is return to Action:
var response = await controller.GetHeroes();
var heroList = (IEnumerable<Hero>)response.Result;
More info here
I want to create a unit test method for the below method which is receiving a file upload "text file" data and parsing it, I tried to use Moq and created a method but I am still very confused in the concept, I need a sample code, I've read many stackover flow questions but it is all for Controllers not Web API
the method used
// Enable both Get and Post so that our jquery call can send data, and get a status
[HttpGet]
[HttpPost]
public HttpResponseMessage Upload()
{
// Get a reference to the file that our jQuery sent. Even if multiple files, they will
// all be their own request and be the 0 index
if (HttpContext.Current.Request.Files.Count>0)
{
HttpPostedFile file = HttpContext.Current.Request.Files[0];
if (file != null && file.ContentLength > 0)
{
try
{
var extension = Path.GetExtension(file.FileName);
if (!IsFileFormatSupported(extension))
{
var objectSerialized = SerializeData(GetError( GlobalResources.NotSupportedFileExtension));
return BadResponse(objectSerialized);
}
var path = SaveFileGetPath(file);
var result = GetPaySlips(path);
var SerializedData = SerializeData(result);
return OkResponse(SerializedData);
}
catch (System.Exception exception)
{
var SerializedData = SerializeData(GetError( GlobalResources.CouldNotReadFile + " " + exception.Message));
return BadResponse(SerializedData);
}
}
else
{
var SerializedData = SerializeData(GetError(file.FileName + " " + GlobalResources.FileisCorrupt));
return BadResponse(SerializedData);
}
}else
{
var SerializedData = SerializeData(GetError( GlobalResources.FileisCorrupt));
return BadResponse(SerializedData);
}
}
the code I did so far
var FileUploadCtrl = new UploadController();
Mock<HttpRequestMessage> cc = new Mock<HttpRequestMessage>();
UTF8Encoding enc = new UTF8Encoding();
// Mock<HttpPostedFileBase> file1 = new Mock<HttpPostedFileBase>();
//file1.Expect(f=>f.InputStream).Returns(file1.Object.InputStream);
//cc.Expect(ctx => ctx.Content).Returns(new retur);
// cc.Expect(ctx => ctx.Content).Returns();
var content = new ByteArrayContent( /* bytes in the file */ );
content.Headers.Add("Content-Disposition", "form-data");
var controllerContext = new HttpControllerContext
{
Request = new HttpRequestMessage
{
Content = new MultipartContent { content }
}
};
//file1.Expect(d => d.FileName).Returns("FileTest.csv");
//file1.Expect(d => d.InputStream).Returns(new HttpResponseMessage(HttpStatusCode.OK)));
var config = new HttpConfiguration();
//var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/upload");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "FileUpload" } });
FileUploadCtrl.ControllerContext = new HttpControllerContext(config, routeData, cc.Object);
var r = FileUploadCtrl.Upload();
Assert.IsInstanceOfType(r, typeof(HttpResponseMessage));
In my Asp.net core 1 app I have controller with following method:
[Microsoft.AspNetCore.Mvc.HttpPost()]
[Microsoft.AspNetCore.Mvc.RequireHttps]
public async System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> Save()
{
if (ModelState.IsValid)
{
try
{
var pass = Request.Form["password"].ToString();
var pass1 = Request.Form["password1"].ToString();
if (!pass.Equals(pass1))
{
return View("~/Views/PasswordRecovery.cshtml");
}
}
catch (System.Exception ex)
{
return View("~/Views/Message.cshtml");
}
}
return View("~/Views/Message.cshtml");
}
I want to write a test for this method. So I have written this:
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Controllers.PasswordRecoveryController(_mockRepo.Object);
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
var collection = new Microsoft.AspNetCore.Http.FormCollection(dic);
controller.Request.Form = collection; //request is null
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/Message.cshtml", viewResult.ViewName);
}
The problem is that I need to set some test values to Form, Form is in Request, and Request is NULL. I can not find, how can I create some not NULL request and fill it's Form with values.
EDIT
Answers helped me to finish up with following solution:
I've created a method that will return a FormCollection:
private Microsoft.AspNetCore.Http.FormCollection GetFormCollection()
{
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
return new Microsoft.AspNetCore.Http.FormCollection(dic);
}
And my test method is:
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Findufix.Controllers.PasswordRecoveryController(_mockRepo.Object);
var httpContext = new Moq.Mock<Microsoft.AspNetCore.Http.HttpContext>();
httpContext.Setup( x => x.Request.Form).Returns(GetFormCollection());
controller.ControllerContext.HttpContext = httpContext.Object;
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/PasswordRecovery.cshtml", viewResult.ViewName);
}
If you pass a DefaultHttpContext to your controller, Request won't be null and you can assign the form to Request.Form. No mocking required.
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Controllers.PasswordRecoveryController(_mockRepo.Object);
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
var collection = new Microsoft.AspNetCore.Http.FormCollection(dic);
// Give the controller an HttpContext.
controller.ControllerContext.HttpContext = new DefaultHttpContext();
// Request is not null anymore.
controller.Request.Form = collection;
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/Message.cshtml", viewResult.ViewName);
}
With Moq, you can do it like this:
var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(c => c.Request.Form).Returns(delegate()
{
var formVaues = new NameValueCollection();
formVaues .Add("Id", "123");
formVaues .Add("Name", "Smith");
return formVaues ;
});
In Moq you can try to use Setup() or SetupGet() to teach it to return something that you need
something along the lines of
controller.SetupGet(x => x.Request.Form).Returns(collection);
For my unit-tests I use Microsoft.VisualStudio.TestTools.UnitTesting and MvcContrib.TestHelper
my action in controller:
public ActionResult index()
{
try
{
Session.Add("username", "Simon");
var lSessionID = Session.SessionID;
return Content(lSessionID);
}
catch
{
}
return Content("false");
}
my unit-test:
[TestMethod]
public void IndexTestMethod1()
{
TestControllerBuilder builder = new TestControllerBuilder();
StartController controller = new StartController();
builder.InitializeController(controller);
var lResult = controller.index();
var lReturn = ((System.Web.Mvc.ContentResult)(lResult)).Content; // returns "false"
Assert.IsFalse(lReturn == "false");
}
When I call the index()-action in my browser, it shows the Session-ID. When I call the action via my unit-test, the lReturn is "false" and not the expected Session-ID.
How can I get the Session.SessionID in my unit-test?
The Session variable is read from ControllerContext.HttpContext.Session , and Session is of the type HttpSessionStateBase.
with in the unit test, you can use set the ControllerContext object. ( or use any mock providers like moq). I have not tested the code
var contextMock = new Mock<ControllerContext>();
var mockHttpContext = new Mock<HttpContextBase>();
var session = new Mock<HttpSessionStateBase>();
mockHttpContext.Setup(h => h.Session).Returns(session.Object);
contextMock.Setup(c => c.HttpContext).Returns(mockHttpContext.Object);