How to perform unit testing on a void method in web api controller - asp.net-mvc

Hi I have a update method in webAPI and that is a void method and I want to perform unit testing on that method.How do I do that??
Not Found any solution.
Below is webapi controller method :-
[HttpPut]
public void UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO)
{
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
}
Below is the unit test case for above method
[TestMethod]
public void UpdatePushNotificationQueue_ShouldUpdate()
{
var item = GetDemoPushNotificationQueue();
var controller = new PushNotificationQueueController();
var result = controller.UpdatePushNotification(item) as ;
Assert.IsNotNull(result);
}
I want what do I write after as in
var result = controller.UpdatePushNotification(item) as ???

The controller method is void so there is no return type and nothing to cast it to.
I believe this to be an XY problem.
void controller actions will always return HTTP Status Code 200 OK at run-time except when an exception is thrown.
Based on the tags in original post the assumption is that the mentioned controller is an ApiController
which means that the controller can be refactored to
[HttpPut]
public IHttpActionResult UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO) {
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
return Ok();
}
There is also the option to wrap it in a try-catch in case of errors
[HttpPut]
public IHttpActionResult UpdatePushNotification(PushNotificationQueueBO pushnotificationqueueBO) {
try {
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
return Ok();
} catch (Exception ex) {
return InternalServerError(ex);
//OR
//return InternalServerError()
}
}
But that is more of a cross-cutting concern that can be handled by action filters.
This would then allow for an actual return type to be asserted
//...omitted for brevity
IHttpActionResult result = controller.UpdatePushNotification(item);
Assert.IsNotNull(result);
The PushNotificationQueueBO business object however, appears to be making a static member call.
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
This makes it difficult to unit test the encapsulated API method call in isolation and may result in undesired behavior.
It is suggested that the static business object call be encapsulated behind an abstraction and implementation that can be replaced by a mock when testing in isolation.

You can test a void function in different ways and it depends on what the void method does. For example, if a void method increments the numeric value of a property of its class, then you can use that property in your test. In your case, your void method performs this action;
PushNotificationQueueBO.UpdatePushNotificationQueue(pushnotificationqueueBO);
Firstly, identify what this action does and what it affects. For example, if this method's action performs a manipulation on a queue object, then you can test this object as a result of the void method.

Related

How to create a unit test that tests that a page requires authorization in mvc5

I am trying to work out how to write a unit test that will test that a controllers authorization is working. IE a user that is not logged in can't access the page. Does anybody know how to go about this? I am having trouble finding examples.
something like this (Pseudo Code)
[TestMethod]
public void Get_Auth_Page()
{
be_a_user_thats_not_logged_in = true;
// Arrange
MyController controller = new MyController();
// Act
var result = controller.Index();
// Assert
if(result.httpstatus == 403)
Assert.True();
}
If you are simply decorating your action method with [Authorize], you can just have a test that asserts the existence of the attribute:
[TestMethod]
public void Index_action_requires_authentication()
{
// If Index is overloaded, you might need to filter by argument list
MethodInfo indexMethod = typeof(MyController).GetMethod("Index");
bool requiresAuthentication =
Attribute.IsDefined(indexMethod, typeof(AuthorizeAttribute));
Assert.IsTrue(requiresAuthentication);
}
Obviously you aren't testing the Authorize implementation here, but it does serve to both document and to protect against developers accidentally removing it.
If you are running custom code, then you are probably returning a HttpStatusCodeResult, so you can just check for that:
public void Index_action_requires_authentication()
{
ActionResult result = new MyController().Index();
HttpStatusCodeResult statusCodeResult = result as HttpStatusCodeResult;
Assert.IsNotNull(statusCodeResult);
Assert.AreEqual(403, statusCodeResult.StatusCode);
}
If you are manually writing to the HttpResponse (Response.StatusCode or Response.Headers), then you'll need to mock the HttpContextBase as others have described.

how to unit test for session variable in controller in mvc

I am unit-testing my controller.
In one of my controller methods I am setting Session variables:
public void Index()
{ Session["foo"] = "bar";
return View();
}
How can I unit-test this? The problem is that the Session property is null when testing. Injecting is not possible because the Session property is readonly.
I don't want to use any third-party tool or mocking.
Simply dont use things like Session["foo"] in your controller methods. Best practice is keep action methods unaware of any context-like global objects. Everything your action method needs should be given to her in form of arguments. Note that built-in mechanism of model binding works exactly like that - you dont use Request.Form[], you let "somebody behind the scene" pass it to your action as argument.
Now for the session you can do the same - write you very simple ValueProvider which will know how to recognize arguments you want to fill from session, and you are done. In production your actions will work with session, in test you cant simply pass them any values you want as arguments.
For inspiration look at this http://www.prideparrot.com/blog/archive/2012/7/how_to_create_a_custom_session_value_provider
Injecting is not possible because the Session property is readonly.
This means you cannot use setter injection, but could you use constructor injection, ie add a constructor for your controller that is something like:
MyController(Session session)
{
m_session = session;
// then call your main constructor
}
Session getSession()
{
return m_session;
}
You can then use this separate constructor during testing.
I agree with #rouen. do not directly use Session["foo"]. But I think having ValueProvider ans might not be a practical solution, as we only store very few variables, and these values may be and most likely not ur full model.
So my approach is something similar to what Vic Smith suggests but a much more IOC (and Mock) friendly.
I would create a provider (i.e a service) to retrieve the session variables
public class SessionVariableProvider : ISessionVariableProvider
{
public object GetSessionValue(string key)
{
if (!HttpContext.Current.Session.IsNewSession
&& HttpContext.Current.Session[key] != null)
{
return HttpContext.Current.Session[key];
}
throw new ArgumentNullException(key);
}
public void SetSessionValue(string key, object value)
{
HttpContext.Current.Session[key] = value;
}
}
public interface ISessionVariableProvider
{
object GetSessionValue(string key);
void SetSessionValue(string key, object value);
}
Modify your Controller expect ISessionVariableProvider as a parameter.
public class TestController: Controller
{
protected readonly ISessionVariableProvider _sessionVariableProvider;
protected InowiaControllerBase(ISessionVariableProvider sessionVariableProvider)
{
Guard.ArgumentNotNull(sessionVariableProvider, "sessionVariableProvider");
this._sessionVariableProvider = sessionVariableProvider;
}
public ActionResult Index()
{
_sessionVariableProvider.SetSessionValue("foo", "bar");
var foo2 = (string)_sessionVariableProvider.GetSessionValue("foo2");
return View();
}
}
when testing create your own test implementation of ISessionVariableProvider and pass it to the controller.

How to use HandleError with model state errors

I want to use a custom action filter to handle specific exceptions from my service classes to populate the model state and then return the view.
For example, take my previous code:
public ActionResult SomeAction(SomeViewModel model)
{
try
{
_someService.SomeMethod(model);
}
catch (ServiceException ex)
{
ModelState.AddModelError(ex.Key, ex.ErrorMessage);
}
return View();
}
Basically, it would call a service, and if a ServiceException was thrown, it would know that there was an issue w/ the model data, and add the error to the ModelState, then just return the view. But I noticed some very un-DRY-like patterns, because I had this same try/catch code in every action method.
So, to DRY it up a bit, I basically created a new HandleServiceError action filter:
public class HandleServiceErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
((Controller)context.Controller)
.ModelState
.AddModelError(
((ServiceException)context.Exception).Key,
((ServiceException)context.Exception).ErrorMessage
);
context.ExceptionHandled = true;
}
}
Then simplified my action methods like so:
public ActionResult SomeAction(SomeViewModel model)
{
_someService.SomeMethod(model);
return View();
}
Problem is, once the action filter handles the error, it doesn't return to my action method. I sort of understand, under the hood, why this is happening. But I would still like to figure out a way to do what I'm trying to do.
Is this possible?
Thanks in advance.
UPDATE:
I tried the suggestions from the article Darin provided in his answer, but ran into issues trying to use constructor injection with the controller's model state.
For example, if you look at their Controllers\ProductController.cs code, they have the controller's empty constructor using a service locator to create the service, passing in the controller's ModelState at that point:
public ProductController()
{
_service = new ProductService(new ModelStateWrapper(this.ModelState),
new ProductRepository());
}
But if you look at the injected constructor, it assumes the ModelState will be injected into the constructor for the service:
public ProductController(IProductService service)
{
_service = service;
}
I don't know how to get CI to work with the current controller's ModelState. If I could figure this out, then this approach may work.
You could still return the corresponding view:
context.Result = new ViewResult
{
ViewName = context.RouteData.GetRequiredString("action")
};
You may also take a look at the following article for an alternative about how to perform validation at the service layer.

How to unit test a custom actionresult

I'm trying to unit test a custom action result. I recently watched Jimmy Bogard's excellent MvcConf video ("put your controllers on a diet") http://www.viddler.com/explore/mvcconf/videos/1/ and have started to try and implement some custom action results. I've managed that without a problem, the ActionResult works fine at runtime but I'm having trouble trying to unit test them.
Unfortunately in the code download there are no unit tests for Jimmy's custom action methods... which make me wonder.
I realise that action methods just return instances of the ActionResult types and its the MVC framework that actually calls the ExecuteResult method, which of course is not available when running the unit test. So my unit test is now just creating an instance of my custom ActionResult and I then call ExecuteResult.
Unfortunatley in the ExecuteResult method of my custom ActionResult it is also calling the ExecuteResult method of a ViewResult that I passed it. At that point it blows up. How should I be mocking/stubbing these things to get my unit test working?
public class SendToAFriendActionResult : ActionResult
{
public const string INVALID_CAPTCHA = "You don't appear to have filled out the two words from the security image correctly to prove you're a human. Please try again.";
public const string INVALID_MODEL_STATE = "You don't appear to have filled out all the details correctly. Please try again.";
public const string CONTACT_FAIL = "Unfortunately we experiend a problem sending the link. Please try again later.";
public const string SEND_TO_A_FRIEND_FAIL_KEY = "ContactFail";
private RedirectResult _success;
private ViewResult _failure;
private readonly SendToAFriendModel _model;
private readonly bool _captchaValid;
private readonly MessageBuilderServiceBase _mbs;
public RedirectResult Success
{
get { return _success; }
set { _success = value; }
}
public ViewResult Failure
{
get { return _failure; }
set { _failure = value; }
}
public SendToAFriendActionResult(RedirectResult success, ViewResult failure, SendToAFriendModel model, bool captchaValid, MessageBuilderServiceBase mbs)
{
_success = success;
_failure = failure;
_model = model;
_captchaValid = captchaValid;
_mbs = mbs;
}
public override void ExecuteResult(ControllerContext context)
{
if (!_captchaValid)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_CAPTCHA;
// On reaching this point I receive the error
// Object reference not set to an instance of an object
// as the MVC framework calls FindView
Failure.ExecuteResult(context);
return;
}
if (!context.Controller.ViewData.ModelState.IsValid)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_MODEL_STATE;
Failure.ExecuteResult(context);
return;
}
_mbs.RecipientEmailAddress = _model.EmailRecipient;
_mbs.SendersName = _model.SendersName;
_mbs.Url = _model.URL;
var result = _mbs.sendMessage();
if (!result)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = CONTACT_FAIL;
Failure.ExecuteResult(context);
return;
}
Success.ExecuteResult(context);
}
}
Here's the start of my unit test ...
IMessageService _emailMessageSerivce;
IGalleryRepository _repository;
var stfModel = new SendToAFriendModel
{
SendersName = "Someone",
URL = "http://someurl.com",
EmailRecipient = "a-friend#somewherelse.com"
};
var failure = new ViewResult() {ViewName ="SendToFriend"};
const bool captchaValid = false;
var fakeControlllerContext = MockRepository.GenerateStub<ControllerContext>(null);
var stf = new SendToAFriendActionResult(null, failure, stfModel, captchaValid, null);
stf.ExecuteResult(fakeControlllerContext);
I've put comments in the SUT to show were the problem occurs.
I know I should be stubbing/mocking somehow but I just can't seem to resolve this.
From ASP.NET MVC 2 In Action (coauthored by Jimmy Bogard):
By taking that hard-to-test code out
of an action and putting it into the
Execute method of an action result,
you ensure that the actions become
significantly easier to unit-test.
That’s because when you unit-test an
action, you assert the type of action
result that the action returns and the
state of the action result. The
Execute method of the action result
isn’t executed as part of the unit
test.
Unit tests are designed to isolate behavior and concerns. You're mixing concerns by calling ExecuteResult from within your custom Action. Instead, I would have the SendToAFriendActionResult return the actual ActionResult (Failure or Success):
public ActionResult GetAction(..)
{
ActionResult result;
//logic here to determine which ActionResult to return
return result;
}
In your Controller:
public ViewResult SendToAFriend()
{
return SendToAFriendActionResult(null, failure, stfModel, captchaValid, null)
.GetAction();
}
This method will allow the MVC framework to do its job and isolates those concerns outside your custom ActionResult. Your test should assert that the correct type of Action, failure or success, is returned based on the parameters you set going in.

How can I unit test HandleUnknownAction() of an ASP.NET MVC Controller?

I have the following HandleUnknownAction set on my base controller class:
protected override void HandleUnknownAction(string action)
{
Response.Redirect("/");
}
How can I unit test that? Another point, is that way to handle the unknown action correct? Seems that calling RedirectToAction() would be more correct but the HandleUnknownAction doesn't have a return value.
The far I could get to test that is:
[Test]
public void TestHandleUnknownAction()
{
ctroler.ActionInvoker.InvokeAction(ctroler.ControllerContext, "unknown");
}
I'm stuck at it.
I don't think there's a need to test that HandleUnknownAction is invoked when a controller is missing an action. We trust the framework to handle that. So we can test the implementation by calling HandleUnknownAction directly with the mocking framework Moq. Should also be possible with Rhino Mocks.
public void TestHandleUnknownAction()
{
Mock<ControllerContext> cc = new Mock<ControllerContext>
(MockBehavior.Strict);
cc.Expect(c => c.HttpContext.Response.Redirect("/"));
TestHelperController controller = new TestHelperController();
controller.ControllerContext = cc.Object;
controller.InvokeUnknownAction("test");
}
Where TestHelperController makes the HandleUnknownAction accessible:
public class TestHelperController : RealController
{
public void InvokeUnknownAction(string action)
{
this.HandleUnknownAction(action);
}
}
That's fine for a simple Response.Redirect but that unit test code will not work if you want to do something more complicated like rendering an error view:
// TODO - Put some stuff into ViewData or a model
View("Error").ExecuteResult(Me.ControllerContext)

Resources