I have ApiController with ExecuteAsync method overriden like this (I am using RavenDB and here I create its session):
public override async Task<HttpResponseMessage> ExecuteAsync(
HttpControllerContext controllerContext,
CancellationToken cancellationToken)
{
using (Session = Store.OpenAsyncSession())
{
var result = await base.ExecuteAsync(controllerContext, cancellationToken);
await Session.SaveChangesAsync();
return result;
}
}
I want to write automated Tests for it using xUnit or Microsoft Test Framework and at the moment I'm stuck with calling controllers action via ExecuteAsync.
Should I construct ControllerContext in any particular way to have that done or am I missing something?
Simply creating controller object and calling actions throws NullReferenceException for Session object, which is obviously not created as the ExecuteAsync has not been called.
Related
I know lots of mocking questions are asked, but none worked with me.
I'm trying to write a unit test for a service I have. The service have the following line of code
var assignments = await _assignmentRepository.WhereAsync(as => as.DepartmentId == departmentId);
Here's the implementation of WhereAsync method:
public async Task<List<T>> WhereAsync(Expression<Func<T, bool>> expression)
{
return await _dbContext.Set<T>().Where(expression).ToListAsync();
}
Here's my mock test statement (listAssignments is a predefined variable):
_assignmentRepository.Setup(rep => rep.WhereAsync(as => It.IsAny<bool>())).ReturnsAsync(listAssignments);
I know that we can't Mock Where and FirstOrDefault methods, but isn't there a way to mock my web service WhereAsync method??
As Tseng mentioned in the comment above. We don't mock the DbContext, we mock the repository itself.
So I used the InMemoryDatabase testing. Added some data to my in-memory database which made my DbContext return the data I want.
var mapOptions = new DbContextOptionsBuilder<MapViewerContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
var identityOptions = new DbContextOptionsBuilder<AppIdentityDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
var mapContext = new MapViewerContext(_configuration.Object, mapOptions);
var appIdentityContext = new AppIdentityDbContext(_configuration.Object, identityOptions);
I'm using MVC5 and NSubstitute. I'm trying to create unit test to verify the model is being properly created for some controller actions.
The problem I have is that the controller is using a model within which I have something along the lines of:
Url = new UrlHelper(Request.RequestContext).Action("Browse")
Any time I try to test this controller method, I get a null object exception. I have tried a lot of different ways to try and mock the context, but it never seems to quite work. Can anyone help?
I've referred to many SO question including;
ASP.NET MVC: Unit testing controllers that use UrlHelper
Which isn't so helpful to me as I'm using NSubstitute.
I was trying to use a MockHttpContext helper method I had found:
public static T MockHttpContext<T>(this T controller) where T : Controller
{
controller.ControllerContext = new ControllerContext
{
HttpContext = GetHttpContextMock()
};
controller.Url = new UrlHelper(controller.ControllerContext.RequestContext);
return controller;
}
private static HttpContextBase GetHttpContextMock()
{
var routes = new RouteCollection();
var actionExecutingContext = Substitute.For<ActionExecutingContext>();
var httpContextBase = Substitute.For<HttpContextBase>();
var httpServerUtilityBase = Substitute.For<HttpServerUtilityBase>();
var httpResponseBase = Substitute.For<HttpResponseBase>();
var httpRequestBase = Substitute.For<HttpRequestBase>();
var httpSessionStateBase = Substitute.For<HttpSessionStateBase>();
actionExecutingContext.HttpContext.Returns(httpContextBase);
httpContextBase.Request.ApplicationPath.Returns("");
httpContextBase.Response.ApplyAppPathModifier(Arg.Any<string>())
.Returns(ctx => ctx.Arg<string>());
httpContextBase.Request.Returns(httpRequestBase);
httpContextBase.Response.Returns(httpResponseBase);
httpContextBase.Server.Returns(httpServerUtilityBase);
httpContextBase.Session.Returns(httpSessionStateBase);
httpRequestBase.Cookies.Returns(new HttpCookieCollection());
return httpContextBase;
}
I was trying to do the same thing. This worked for me:
controller.Url = Substitute.For<UrlHelper>();
controller.Url.Action(Arg.Any<string>(), Arg.Any<string>()).Returns("something");
Then when I call my controller action, it does not crash when dereferencing the UrlHelper. Of course you have to mock up (NSubstitute) the controller properly. I hope this works. I can post more code for mocking the controller if you need.
As you know, inside unit tests it's built-in angularjs feature to mock XHR requests with $httpBackend - this is nice and helpful while writing unit tests.
Recently, I met with need of mocking XHR in case of file upload and discovered some problems.
Consider following code:
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress(event), false);
xhr.addEventListener("load", uploadComplete(event), false);
xhr.addEventListener("error", uploadError(event), false);
xhr.addEventListener("abort", uploadAbort(event), false);
xhr.open("POST", 'some url');
xhr.send(someData);
What I want to do is to do unit testing of such a code with mocking of XHR requests, but it's not possible do it because there is no $http service used here.
I tried this (and it's working and could be mocked with $httpBackend):
$http({
method: 'POST',
url: 'some url',
data: someData,
headers: {'Content-Type': undefined},
transformRequest: angular.identity})
.then(successCallback, errorCallback);
But in this case I don't know how to implement 'progress' callback and 'abort' callback (they are essential and required in case I am working on now).
I've seen information that latest Angular supports progress callback for promises (not sure though whether it's integrated with $http service), but what about abort callback?
Any ideas or maybe your met with something similar before?
If the $http service doesn't give you everything you need, you can still unit test the first block of code. First of all, change your code to use Angular's $window service. This is just a wrapper service, but it allows you to mock the object in your tests. So, you'll want to do this:
var xhr = new $window.XMLHttpRequest();
Then in your tests, just mock it and use spies.
$window.XMLHttpRequest= angular.noop;
addEventListenerSpy = jasmine.createSpy("addEventListener");
openSpy = jasmine.createSpy("open");
sendSpy = jasmine.createSpy("send");
xhrObj = {
upload:
{
addEventListener: addEventListenerSpy
},
addEventListener: addEventListenerSpy,
open: openSpy,
send: sendSpy
};
spyOn($window, "XMLHttpRequest").andReturn(xhrObj);
From there, you can make the different spies return whatever you want for the different tests.
You should mock $http and control any deferred, as you want more control over your test. Basically, mock $http provider and serve a custom implementation that exposes its deferred, then play with it.
You should not worry whether $http is working right or not, because it is supposed to, and is already tested. So you have to mock it and only worry testing your part of the code.
You should go something like this:
describe('Testing a Hello World controller', function() {
beforeEach(module(function($provide) {
$provide.provider('$http', function() {
this.$get = function($q) {
return function() {
var deferred = $q.defer(),
promise = deferred.promise;
promise.$$deferred = deferred;
return promise;
}
};
});
}));
it('should answer to fail callback', inject(function(yourService, $rootScope) {
var spyOk = jasmine.createSpy('okListener'),
spyAbort = jasmine.createSpy('abortListener'),
spyProgress = jasmine.createSpy('progressListener');
var promise = yourService.upload('a-file');
promise.then(spyOk, spyAbort, spyProgress);
promise.$$deferred.reject('something went wrong');
$rootScope.$apply();
expect(spyAbort).toHaveBeenCalledWith('something went wrong');
}));
});
And your service is simply:
app.service('yourService', function($http) {
return {
upload: function(file) {
// do something and
return $http({...});
}
};
});
Just note that promises notification is only available in the latest RC release. So, if you can't use it, just elaborate a little more the example and mock the XHR events and so.
Also note that you should preferably have one test case for each of the callbacks (fail, success and progress), in order to follow KISS principle.
I am trying to unit test HttpStatusCodes in MVC4 but I keep getting a 'System.NullReferenceException' when the controller tries to set the status code on Response, which makes sense as the action is getting called directly. I cant for the life of me work out how to do it without it becoming an integration test. Somebody must have done this, any ideas? See my existing code below.
Controller
public ActionResult Index()
{
Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
Response.Headers.Add("Retry-After", "120");
return View();
}
Test
[Test]
public void IndexActionShouldReturn503StatusCode()
{
//Given
var controller = new HomeController();
//When
var result = controller.Index() as HttpStatusCodeResult;
//Then
result.StatusCode.Should().Be((int)HttpStatusCode.ServiceUnavailable);
}
Note
The requirement is for a friendly 'site down' page so I need to return both a view and the status code.
You're returning a ViewResult, then trying to cast it as a HttpStatusCodeResult in your unit test. Try returning a HttpStatusCodeResult instead of a view.
public ActionResult Index()
{
Response.Headers.Add("Retry-After", "120");
return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable);
}
I have a situation where I'm debating how to architect my controllers.
Consider the following controller:
public class FileSharingController : Controller
{
private readonly ICommandBus commandBus;
public FileSharingController(ICommandBus commandBus)
{
this.commandBus = commandBus;
}
[HttpPost]
public ActionResult PrepareMetadata(int blocksCount, string fileName, long fileSize)
{
...
}
[HttpPost]
public ActionResult ClearFileMetadata(string fileName){
...
}
[HttpPost] [ValidateInput(false)] //$.ajax({ data: html5FormDataFileChunk , processData: false ... })
public ActionResult UploadBlock(string fileName, int blockId){
var fileUploadCommand = (FileUploadCommand)ExtractFromSessionData(fileName);
var result = commandBus.Submit(fileUploadCommand);
...
}
public ActionResult CommitFileUploads(string[] filesToCommit){
var commitFileUploadCommand = (CommitFileUploadCommand)ExtractFromSessionData(fileName);
var result = commandBus.Submit(commitFileUploadCommand );
...
}
In this controller, I use the command pattern and pass a model to my commandBus which interfaces with my domain. The first three [HttpPost] methods on the controller are for handling jQuery ajax calls from a responsive file uploading UI.
Consider the situation where a user fills out a form (an interview) and uploads some files along with it. Although the user can upload the files before submitting the form, I don't want the uploaded files to be committed until AFTER they submit the form and it passes validation. That is why the last method on the controller is not an http endpoint. As such I have the following controller:
public class InterviewController : Controller
{
[HttpGet]
public ActionResult UserInterview()
{
InterviewViewModel viewModel = new InterviewViewModel ();
return PartialView(viewModel);
}
[HttpPost] [AllowAnonymous]
public ActionResult UserInterview(InterviewViewModel viewModel)
{
if(ModelState.IsValid)
{
var fileSharingController = new FileSharingController();
fileSharingController.CommitFileUploads(viewModel.Files);
}
return PartialView(viewModel);
}
}
The problem is I'm using IoC to inject a commandBus into the FileSharingController so I cannot just instantiate it with default constructor as I am doing.
My options to consider:
Create a custom controller factory to allow instantiating my controller anywhere in the code.
Turn my FileSharingController in a WebAPI controller and treat as a service
Which is the better design path for this situation? If the latter case, how can I keep the CommitFileUploads() method private? I don't want it to be exposed as an endpoint that can be triggered without first validating the rest of the form.
You can instantiate your controller like this:
ICommandBus commandBus = DependencyResolver.Current.GetService<ICommandBus>();
var fileShareController = new FileSharingController(commandBus);
Generic GetService() method is extension method, so make sure that you have "using System.Web.Mvc;" line in the cs file.
But then, it's better to have helper class that is responsible for keeping/storing already uploaded files, and call it from both controllers, instead instantiating controllers manually.
For example:
public class FileUploadManager
{
public FileUploadManager(ICommandBus commandBus, HttpSessionStateBase sessionState)
{
//....
}
}
and then you call it:
ICommandBus commandBus = DependencyResolver.Current.GetService<ICommandBus>();
var fileShareController = new FileUploadManager(commandBus, this.HttpContext.Session);
Or, if you don't want to use DependencyResolver, you pass ICommandBus to both controller's constructors, and use that reference to instantiate helper class.
simply just create the object of another conroller and use all its public methods.