Here is the scenario:
I'm writing a test for my controller and need to setup a view model titled CheckoutViewModel. My controller method, Products does not take CheckoutViewModel as a parameter, so I cannot pass it in that way.
Currently, the test fails returning a Null Exception because CheckoutViewModel is not getting set and called.
Question: How can I setup my CheckoutViewModel with data.
Error Details:
System.NullReferenceException
Object reference not set to an instance of an object
Current Test
[TestMethod]
public void Products_ProductControllerIsCalled_ReturnsViewWithProducts()
{
// Arrange
var currentSession = _autoMoqer.GetMock<ICurrentSession>().Object;
ProductController productController = new ProductController(currentSession);
var checkoutViewModel = new CheckoutViewModel
{
CheckoutId = new Guid()
};
// Act
ActionResult result = productController.Products();
// Assert
Assert.IsInstanceOfType(result, typeof(ViewResult));
}
Controller
[AccectReadVerbs]
public ActionResult Products()
{
CheckoutViewModel checkoutViewModel = GetCheckoutViewModel();
var checkoutId = checkoutViewModel.CheckoutId;
var result = _productOrchestrator.Products(checkoutId, currentSession)
return View(result);
}
Failing on this method
private CheckoutViewModel GetCheckoutViewModel()
{
if(Session["CheckoutViewModel"] == null)
{
return new CheckoutViewModel();
}
return (CheckoutViewModel)Session["CheckoutViewModel"];
}
If GetCheckoutViewModel has some dependencies on i.e services, dbConnection or other complex classes, you need to add a class with an interface, move the method for GetCheckOutViewModel to the class and take the new interface as a dependency to the controller. Then you need to mock the new interface.
Or edit your viewmodel to take interface dependencies on the stuff that stands in the way of unit testing, i.e the Session.
I think you could create some interface:
public interface ISessionManager
{
Session session {get; set;}
}
Then your controller constructor:
public ProductsController(ISessionManager sm)
{
_sessionManager = sm;
}
Then you can pass a mocked instance to your controller.
I'm guessing that the exceptions is due to the fact that when you're running the unit test there will not be any (webserver) session available. What you want do is to isolate your tests from any external dependencies - and a session state that is part of the webserver hosting environment would be an external dependency.
To solve this you need to either mock or stub out the Session object from your test. There are many ways to do this, but the easiest way would be to make Session a public property on the Controller. From your test you would then set the Session to an instance you create within your test.
Related
I have the following test method for an Asp.Net controller action method.
[TestMethod]
public void Get()
{
var controller = new MyController();
var result = controller.GetAListOfRecords();
Assert.IsNotNull(result);
Assert.IsTrue(result.Count() > 0);
}
And the action is
[Authorize(Roles = "Users")]
public IQueryable<MyModel> GetAListOfRecords()
{
var user = User.Identity.Name; // null when called from the test method
var q = from t in ........
select t;
return return string.IsNullOrEmpty(user) ? Enumerable.Empty<MyModel>().AsQueryable() : q;
}
However, the var user = User.Identity.Name will not get current user name in the test class (it will actually be assigned an empty string). Is it possible to set user in the test method?
Update:
I tried the following in the test class but .....Identity.Name is read-only.
controller.RequestContext.Principal.Identity.Name = #"mdynycmas\wangyi";
Actually, you're using the User property on your Controller object. This property is using the underlying HttpContext.Current. You need to mock the HttpContextBase and return a test user, see for instance How to mock Controller.User using moq.
You need to implement some mocks in your unit tests. Also I suggest you to remove your direct dependency of HttpContext, and better create a wrapper object instead, so you can mock it easily. See more information in this link about this approach.
Hope it helps!
I have a web api 2 web service get method. Inside I'm using HttpContext.Current.Request.UserHostAddress. When calling my controller method directly int he unit test this isn't filled in so is errors with null object. So I searched for how to fill this in and found the following which helped with that issue: Add IP address to HttpRequestMessage
However, this needs a server name to send the request to. The problem is that when tests run the VSExpress will need to be running for this API web service, which it won't be when just running the tests. On top of that even if it was it seems it picks a random port to run on so I couldn't hardcode the address like he does in the above link. How can I test my api 2 method given the above issues?
This is the line that blows up when I just test the api method
string ip = HttpContext.Current.Request.UserHostAddress;
[EDIT] Answer
Just so everyone knows here is the solution in code
public class MyController : ApiController
{
private: HttpRequestBase httpRequest;
public MyController()
{
httpRequest = new HttpRequestWrapper(HttpContext.Current.Request)
}
public MyController(HttpRequestBase http)
{
httpRequest = http;
}
public HttpResponseMessage Get()
{
string ip = httpRequest.UserHostAddress;
}
}
I use Moq in the unit test:
Mock<HttpRequestBase> httpRequestMock = new Mock<HttpRequestBase>();
httpRequestMock.Setup(x => x.UserHostAddress).Returns("127.0.0.1");
// then pass httpRequestMock.Object to my controller ctor and good to go
Decouple your controller from the HTTP context. There might be some built-in functionality to do this with which I'm unfamiliar, but one approach would be to simply inject a mockable object. Consider something like this:
public interface IRequestInformation
{
string UserHostAddress { get; }
}
public class RequestInformation : IRequestInformation
{
public string UserHostAddress
{
get { return HttpContext.Current.Request.UserHostAddress; }
}
}
Now you've abstracted the dependency on HttpContext behind an interface. If you're using dependency injection, inject that interface into your controller. If you're not, you can fake it:
// in your controller...
private IRequestInformation _request;
public IRequestInformation RequestInfo
{
get
{
if (_request == null)
_request = new RequestInformation();
return _request;
}
set { _request = value; }
}
Then use that in your controller logic:
string ip = RequestInfo.UserHostAddress;
Now in your unit tests you can supply a mock/fake/etc. for the RequestInfo property. Either create one manually or use a mocking library. If you create one manually, that's simple enough:
public class RequestInformationFake : IRequestInformation
{
public string UserHostAddress
{
get { return "some known value"; }
}
}
Then just supply that to the controller when arranging the test:
var controller = new YourController();
controller.RequestInformation = new RequestInformationFake();
// run your test
Replace your references to HttpContext by references to HttpContextBase. When in your code, initialize the HttpContextBase with a HttpContextWrapper instance, which is a the default behavior implementation in a web stack.
However in your test inject a custom HttpContextBase implementation where you implement the methods and behaviors needed by your test only.
As precised in the link:
The HttpContextBase class is an abstract class that contains the same
members as the HttpContext class. The HttpContextBase class enables
you to create derived classes that are like the HttpContext class, but
that you can customize and that work outside the ASP.NET pipeline.
When you perform unit testing, you typically use a derived class to
implement members with customized behavior that fulfills the scenario
you are testing.
Add the following method to the controller, or inject the equivalent. It uses the magic string MS_HttpContext because that's what the AspNetWebStack implementation uses for exactly the same purpose.
HttpContextBase HttpContextBase => HttpContext.Current != null
? new HttpContextWrapper(HttpContext.Current)
: (HttpContextBase)Request.Properties["MS_HttpContext"]
Replace all other uses of HttpContext.Current in the controller with HttpContextBase.
When unit testing:
var context = new Mock<HttpContextBase>();
...
controller.Request = new HttpRequestMessage();
controller.Request.Properties["MS_HttpContext"] = context.Object;
I'm a beginner at writing unit tests and I have a test I'm trying to get working. I'll start of by explaining what I'm trying to test.
I'm trying to test a method which saves messages in a Mvc 4 project. The method is called SaveMessage and is shown below.
namespace ChatProj.Service_Layer
{
public class UserService : IUserService
{
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
private IMessageRepository _messageRepository;
-> public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
The _messageRepository.Save in the SaveMessage method is implemented in my DAL layer MessageRepository and looks like this:
public void Save()
{
context.SaveChanges();
}
This way of saving will seem a bit overcomplicated, but I structured the project this way because I didn't want the service layer (IUserService & UserService) to handle operations that could & should (i think) be handled by the Data Access Layer (IMessageRepository & MessageRepository).
Now comes the tricky part. I've been trying to understand how I could unit test this. This is my try:
namespace ChatProj.Tests
{
[TestFixture]
class MessageRepositoryTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void SaveMessage_SaveWorking_VerifyUse()
{
//Arrange
var userServiceMock = new Mock<UserService>();
var message = new Message { MessageID = 0, Name = "Erland", MessageString = "Nunit Test", MessageDate = DateTime.Now };
var repositoryMock = new Mock<IMessageRepository>();
var contextMock = new Mock<MessageContext>();
MessageRepository messageRepository = new MessageRepository(contextMock.Object);
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
//Assert
repositoryMock.Verify(m => m.Save());
userServiceMock.Verify(m => m.SaveMessage(message));
}
}
I get this error: Imgur link , and I'm not quite sure how to solve it. I've tried looking at several other SO posts but I fail to make the test work.
So I'm wondering, how do I practically get my Unit Test to work?
You should setup your MessageContext properties to return fake data and don't make real Db call with SaveChanges method.
Right now it still tries to access a real DB.
But you can setup only virtual properties or if it will be an inteface.
So the best solution is to extract an interface from your MessageContext and inject it into repository. Then you can easily mock your IMessageContext interface and force it to return appropriate in-memory data.
Take a look at these two lines:
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
You're creating a userService instance, and then immediately saving your message. Now jump into the SaveMessage code.
public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
Ok, now you're adding stuff to messageContext, and then calling _messageRepository.Save(). But where are messageContext and _messageRepository instantiated?
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
You're creating them at instantiation. The mocks that you've created in your test aren't being used. Instead of creating instances of these objects in the constructor, you might consider passing them into the UserService constructor as arguments. Then, you can pass in mocked instances in your test.
I'm implementing an oauth provider using DotNetOpenAuth CTP library. So I have created an mvc3 application, which has an OAuth Controller with 3 methods in it with the purpose of authorizing third party applications. The controller has an IOAuthService which encapsulates all the logic that the library must do to complete certain tasks, however, the service methods return DotNetOpenOAuth objects that have their constructors protected.
I would like to test the behavior of the methods within my OAuthController, for this, I'm trying to mock my service methods but I havent't been able to do this. I have to tell moq library what type of object I'm expecting the service method to return, and since I cannot access constructors of these objects, I'm not able to perform a test over my controller method.
The controller:
public class OAuthController : Controller
{
private readonly IOAuthService _oAuthService;
public OAuthController(IOAuthService oAuthService)
{
_oAuthService = oAuthService;
}
[Authorize, AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Authorize()
{
ClientApplication requestingClient;
var request = _oAuthService.ReadAuthorizationRequest();
if (request == null)
{
throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request.");
}
var response = _oAuthService.RequestClientAuthorization(GetIdentity().Name, out requestingClient, request);
if (response != null)
{
return response.AsActionResult();
}
var model = new AuthorizeClientApplicationViewModel
{
ClientApplication = requestingClient.Name,
Scope = request.Scope,
AuthorizationRequest = request,
};
return View(model);
}
public virtual IIdentity GetIdentity()
{
return User.Identity;
}
}
I want to test that whenever a third party app has no authorization, a view will pop up to the user asking for his permission to authorize the app. Fot this i need to mock:
_oAuthService.RequestClientAuthorization
The setup of my test method will then look like :
var oAuthService = new Mock<IOAuthService>();
oAuthService.Setup(a => a.RequestClientAuthorization(userName, out client, pendingRequest)).Returns(new OutgoingWebResponse()); // DotNetOpenAuth doesn't allow me to do the **new OutgoingWebResponse**
PD: For this question I only wrote one of the controller methods, but there are 3, and they have similar scenarios.
One possibility is to write a wrapper (the same way ASP.NET MVC abstracts all the HTTP Context specific stuff):
public abstract class OutgoingWebResponseWrapperBase
{
protected OutgoingWebResponseWrapperBase() { }
public abstract ActionResult AsActionResult();
}
and then have a naïve implementation:
public class OutgoingWebResponseWrapper: OutgoingWebResponseWrapperBase
{
private readonly OutgoingWebResponse _response;
public OutgoingWebResponseWrapper(OutgoingWebResponse response)
{
_response = response;
}
public override ActionResult AsActionResult()
{
return _response.AsActionResult();
}
}
Now modify the IOAuthService.RequestClientAuthorization method to return a OutgoingWebResponseWrapperBase instead of OutgoingWebResponse.
Just like that:
public interface IOAuthService
{
...
OutgoingWebResponseWrapperBase RequestClientAuthorization(...);
}
Obviously your controller code will stay absolutely the same. It's just that now you can mock the return type of the RequestClientAuthorization in your unit test because it is an abstract class. You can also mock the AsActionResult abstract method call to return some expected mocked instance and you will assert in your unit test that the controller action that you are testing returned this expected action result.
If the constructor is protected, then a derived type could access it. Can you simply use Moq to create a mock of OutgoingWebResponse (which internally will make Moq derive from it and call the protected constructor I think) and return that from your mock method implementation?
Something like this:
System.Net.HttpWebResponse mockResponse; // get this from somewhere
new Moq.Mock<DotNetOpenAuth.Messaging.OutgoingWebResponse>(mockResponse, 5);
This should let you mock up an OutgoingWebResponse. The next problem becomes, where do you get yoru HttpWebResponse instance, since that too has only a protected constructor. You could continue the chain and mock up that the same what as OutgoingWebResponse, and see how far you get.
I am attempting to unit test my controllers, and I am using the default MVC 3 AccountController unit tests as a basis. So far I have my controller, which looks like:
public partial class HomeController : MyBaseController
{
public HomeController(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; }
public virtual ActionResult Index()
{
return View();
}
public virtual ActionResult About()
{
return View();
}
}
MyBaseController has the following code:
public class MyBaseController : Controller
{
public MyJobLeadsBaseController()
{
CurrentUserId = 0;
}
protected override void Initialize(RequestContext requestContext)
{
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
base.Initialize(requestContext);
// If the user is logged in, retrieve their login id
var user = MembershipService.GetUser();
if (user != null)
CurrentUserId = (int)user.ProviderUserKey;
}
public int CurrentUserId { get; set; }
public IMembershipService MembershipService { get; set; }
protected IUnitOfWork _unitOfWork;
}
Everything works correctly when I run the actual site, and breakpoints show that the Initialize() is correctly being triggered. However, the following unit test never runs the Initialize(RequestContext) method:
[TestMethod]
public void Index_Shows_When_Not_Logged_In()
{
// Setup
HomeController controller = new HomeController(_unitOfWork);
controller.MembershipService = new MockMembershipService(null);
SetupController(controller);
// Act
ActionResult result = controller.Index();
// Verify
Assert.IsNotNull(result, "Index returned a null action result");
Assert.IsInstanceOfType(result, typeof(ViewResult), "Index did not return a view result");
}
protected static void SetupController(Controller controller)
{
RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
controller.Url = new UrlHelper(requestContext);
controller.ControllerContext = new ControllerContext
{
Controller = controller,
RequestContext = requestContext
};
}
Debugging through this unit test shows that at no point does the overridden MyBaseController.Initialize() get called at all. This causes issues where my CurrentUserId property is not being set in unit tests, but is being set on the live system.
What else do I have to do to trigger the Initialize() to be called?
When you make a request to a controller through a website the MVC framework picks up that request and runs it through a number of different steps. Somewhere in that pipeline of steps MVC knows that it must call the Initialize method so it finds the Initialize in your MyBaseController class and executes it. At this point all is well and everything works.
When you create a new instance of your HomeController in your test code you're simply creating a new instance of a class. You're not running that class through the MVC request pipeline and your test framework doesn’t know to execute the Initialize method so you get an error.
Personally I would look to test the Index action independently of the Initialize method. Your Index action above is empty so I can't see exactly what you're trying to do but if you are returning one view for logged in users and another for anonymous users I'd do something like this:
[TestMethod]
public void Index_Shows_When_Not_Logged_In(){
HomeController controller = new HomeController(_unitOfWork);
controller.CurrentUserId=0;
controller.Index();
//Check your view rendered in here
}
[TestMethod]
public void Some_Other_View_Shows_When_Logged_In(){
HomeController controller = new HomeController(_unitOfWork);
controller.CurrentUserId=12; //Could be any value
controller.Index();
//Check your view rendered in here
}
There are some pretty nifty test helpers in the MVC contrib project (http://mvccontrib.codeplex.com/) which allow you to do things like:
controller.Index().AssertViewRendered();
The constructor does not call Initialize. IIRC What calls initialize is the Execute/ExecuteCore method.
Here is the code from the MVC source:
protected virtual void Execute(RequestContext requestContext) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null) {
throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
}
VerifyExecuteCalledOnce();
Initialize(requestContext);
using (ScopeStorage.CreateTransientScope()) {
ExecuteCore();
}
}
This is basically called from the MvcHandler in the BeginProcessRequest method.
Update:
RequestContext does not exist in a unit test, because you're not going through ASP.NET. If I remember correctly you'd need to mock it.
Another problem in using Initialize in a test is the membership provider, which I haven't used myself, but I would guess that AccountMembershipService would fail you in a test? It seems to me that this would create fragile tests. It would probably also slow you down if it has to contact a server, and might fail you on a CI server.
IMO,from a basic look around the Init method, it shouldn't be there. The easiest solution,without breaking anything, that comes to mind is using dependency injection to inject the CurrentUserId in the Controller ctor.
In the case where you "run the actual site" I suspect it calls the default constructor of your HomeController which in turn will call the base controller's correpsonding ctor.
Try modifying your custom ctor of public HomeController(IUnitOfWork unitOfWork) to
public HomeController(IUnitOfWork unitOfWork):base()
This will make sure to call the base controller's default ctor whenever it is called.