I have problems with testing post method.
In that method AntiForgeryToken is checked and I don't know to how to mock it.
Beside that everythings works.
Here my method I want to test:
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="Id,Name,Manufacturer,CatalogNo")] Device device)
{
ValidateRequestHeader(Request);
if (String.IsNullOrWhiteSpace(device.Name)||String.IsNullOrWhiteSpace(device.Manufacturer)||String.IsNullOrWhiteSpace(device.CatalogNo))
{
ModelState.AddModelError("", "Niepoprawne dane");
return PartialView(device);
}
unitOfWork.deviceRepository.Insert(device);
unitOfWork.Save();
return Json(new { ok = true, newurl = Url.Action("Index") });
}
I have already mocked:
private IUnitOfWork fakeRepo;
DeviceController DC;
[TestInitialize]
public void Initialize()
{
Mock<IUnitOfWork> mock = new Mock<IUnitOfWork>();
mock.Setup(m => m.deviceRepository.Get(
It.IsAny<List<Expression<Func<Device, bool>>>>(),
It.IsAny<Func<IQueryable<Device>, IOrderedQueryable<Device>>>(),
null))
.Returns(new[] { new Device { Id = 1, Manufacturer = "a", Name = "b", CatalogNo = "x" } });
mock.Setup(m => m.deviceRepository.Get()).Returns(new[] { new Device { Id = 1, Manufacturer = "a", Name = "b", CatalogNo = "z" } });
fakeRepo = mock.Object;
DC = new DeviceController(fakeRepo);
}
But I'dont know how to method testing AntiforgeryToken
here code:
public void ValidateRequestHeader(HttpRequestBase request)
{
string cookieToken = "";
string formToken = "";
if (request.Headers["RequestVerificationToken"] != null)
{
string[] tokens = request.Headers["RequestVerificationToken"].Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
I can't comment out this call testing purpose but this solution seems little ugly.
Can You suggest any edits?
It looks like you need to mock HttpRequestBase and AntiForgery.
How do you mock them?
You wrap them in your own interfaces exposing the behaviour you need or may need in the very near future. In your production code you provide the real .NET implementations, in your tests, your mocks.
MSDN says AntiForgery.Validate
Validates that input data from an HTML form field comes from the user
who submitted the data.
the signature that takes two string arguments returns void.
Your mock IAntiForgeryValidator would have a Validate(string, string) method that also returns void.
public interface IAntiForgeryValidator
{
void Validate(string cookieToken, string formToken);
}
public class AntiForgeryValidator : IAntiForgeryValidator
{
public void Validate(string cookieToken, string formToken)
{
AntiForgery.Validate(cookieToken, formToken);
}
}
You can use a call back for void methods and verify that they were called the correct number of times:
antiForgeryMock.Setup(m => m.Validate(
It.IsAny<string>(),
It.IsAny<string>()))
.Callback((string cookieToken, string formToken) =>
{
// call back
});
antiForgeryMock.Verify(m => m.Validate(
It.IsAny<string>(),
It.IsAny<string>()), Times.Once());
You have another option (cheating) and that is to stub your call to ValidateRequestHeader() in Create(). This will enable you to test the rest of your code but is not reccomended because the real ValidateRequestHeader() could cause trouble in your production code if you leave the method un-tested.
Related
I have the following controller which retrieves data from an endpoint.
It can also sort data depending on whether or not type is set.
What would be the best approach to testing it?
public class UsersController : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetAllUsers(string type)
{
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("http://demo10102020.mockable.io");
var response = await client.GetAsync($"/people");
response.EnsureSuccessStatusCode();
var stringResult = await response.Content.ReadAsStringAsync();
List<User> rawUsers = JsonConvert.DeserializeObject<User[]>(stringResult).ToList();
List<User> sortedUsers = rawUsers;
if(type == "first-name")
{
sortedUsers = rawUsers.OrderBy(o => o.FirstName).ToList();
}
else if(type == "score")
{
sortedUsers = rawUsers.OrderBy(o => o.Score).ToList();
}
return Ok(sortedUsers);
}
catch (HttpRequestException httpRequestException)
{
return BadRequest($"Error getting users: {httpRequestException.Message}");
}
}
}
}
This is my approach so far, I am not sure how to mock the API:
[TestClass]
public class TestPersonController
{
[TestMethod]
public void GetAllPersons_ShouldReturnAllProducts()
{
var testPersons = GetTestPersons();
var controller = new PersonController();
}
private List<Person> GetTestPersons()
{
var testPersons = new List<Person>();
testPersons.Add(new Person { FirstName = "dfdfdf", Surname = "dfdfdf", Score = 100 });
testPersons.Add(new Person { FirstName = "dfsdfsfasf", Surname = "safasfsdaf", Score = 200 });
testPersons.Add(new Person { FirstName = "asffas", Surname = "asdffasdf", Score = 200 });
return testPersons;
}
}
Not sure if its worth but i would go about, moving the api call to a separate factory class and the sorting. Controller to only get the final value from your factory. I may have an orchestrator factory to call the api invoking factory and do the sorting.
Then, the Unit test can be done for
api factory by extending HttpClient and create methods to return test/mock data.
orchestrator factory to expect the list of strings or input data and test the sorted data.
then comes the test for the controller
just as an opinion, I think your method does too many things
I would put in another method the call Http
For example :
[HttpGet]
public async Task <IActionResult> GetAllUsers (string type)
{
List <User> rawUsers = UserExternalService.GetAllUser()
if (type == "first-name")
{
return Ok (rawUsers.OrderBy (o => o.FirstName) .ToList ());
}
else if (type == "score")
{
return Ok (rawUsers.OrderBy (o => o.Score) .ToList ());
}
return Ok (rawUsers);
}
This method would take 3 unit test
Example:
public class The_Method_GetAllUsers
{
[Fact]
public async void Should_return_user_when_type_is_name
{
Assert.IsType<OkObjectResult>>(this.Sut.GetAllUser("name"));
}
[Fact]
public async void Should_return_user_when_type_is_score
{
Assert.IsType <OkObjectResult>>(this.Sut.GetAllUser("score"));
}
[Fact]
public async void Should_return_user_when_type_its_not_name_and_score
{
Assert.IsType <OkObjectResult>>(this.Sut.GetAllUser("surname"));
}
}
I think it is a cleaner solution, it would be missing the unit test of the service that we believe is only called http and with its try / catch
Mock HttpClient Mock HttpClient using Moq
We are creating unit tests for an application and ran into a problem creating certain tests.
We are unit testing the following Handle() method of the class ActivateCommandHandler:
public class ActivateCommand : IRequest<HttpResponseMessage>
{
public string Controller { get; set; }
public ActivateCommand(string controllername)
{
this.Controller = controllername;
}
}
public class ActivateCommandHandler : CommandHandler<ActivateCommand, HttpResponseMessage>
{
protected readonly ICommandsGateway _commandsGateway;
protected readonly EndpointSettings _endpoints;
protected readonly IUserProfile _userprofile;
public ActivateCommandHandler(IMediator mediator, ICommandsGateway commandsGateway, IOptions<EndpointSettings> endpoints, IValidationContext validationContext, IUserProfile currentUser) : base(mediator, validationContext, currentUser)
{
_commandsGateway = commandsGateway;
_endpoints = endpoints.Value;
_userprofile = currentUser;
}
public override async Task<HttpResponseMessage> Handle(ActivateCommand command, CancellationToken cancellationToken)
{
if (_endpoints.EndpointExists(command.Controller))
{
// Check whether the second server controller is deactivated
string peercontroller = _endpoints.GetPeerController(command.Controller);
if (!string.IsNullOrEmpty(peercontroller))
{
BaseRedundancySwitchStatus controllerStatus = await _commandsGateway.GetRedundancySwitchStatus(_endpoints.GetEndpointAddress(peercontroller));
if ((controllerStatus.CurrentState == "Activated") || (controllerStatus.CurrentState == "ActivatedWithWarning") || (controllerStatus.CurrentState == "Activating"))
{
var resp = new HttpResponseMessage(HttpStatusCode.Conflict)
{
Content = new StringContent($"{peercontroller},{controllerStatus.CurrentState}")
};
return resp;
}
}
var result = await _commandsGateway.PostActivateCommand(_endpoints.GetEndpointAddress(command.Controller));
return result;
}
else
{
throw new InvalidControllerNameException($"ERROR: The controller {command.Controller} does not exist as an endpoint on this Control Center!");
}
}
}
For this the following were mocked: _endpoints, command and _commandsGateway (interface). This works great for unit testing the parameter validation. But we now want to test the behaviour when the peercontroller status is set to a specific value.
To do this we are trying to mock out the function _commandsGateway.GetRedundancySwitchStatus(). The following is the actual test implementation. We mock the _commandsGateway.GetRedundancySwitchStatus() function to return the expected BaseRedundancySwitchStatus with CurrentState set to "Activated". After that we call the handler of the actual function to be tested and check whether we get the expected error.
[Fact]
public async void ShouldHaveErrors_PeerControllerStateActivated()
{
var command = new ActivateCommand("Server Controller Slave1");
BaseRedundancySwitchStatus result = new BaseRedundancySwitchStatus()
{
CurrentState = "Activated"
};
_commandsGateway
.Setup(s => s.GetRedundancySwitchStatus("Server Controller Slave1"))
.ReturnsAsync(result);
HttpResponseMessage res = await _handler.Handle(command, CancellationToken.None);
Assert.True(res.StatusCode == System.Net.HttpStatusCode.Conflict);
}
Debugging the code, when I step through the code in the Handle() method where _commandsGateway.GetRedundancySwitchStatus is called, I can see that _endpoints.GetEndpointAddress(command.Controller) (which is the parameter) is called and the correct value is returned. After this the debugger steps to the next line without any indication of having executed the mock GetRedundancySwitchStatus() function. Inspecting the controllerStatus variable the value is null. I would expect the value to be the BaseRedundancySwitchStatus object which is supposed to be returned by the mocked GetRedundancySwitchStatus() function.
Where are we going wrong?
I am trying to write unit test case using MsTest for custom filter which has the logic to validate the Antiforgerytoken for POST method in ASP.NET WEB API 2 project.
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class ValidateJsonAntiForgeryTokenAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
try
{
string cookieToken = null;
string formToken = null;
if (actionContext.Request.IsAjaxRequest())
{
IEnumerable<string> tokenHeaders;
if (actionContext.Request.Headers.TryGetValues("__RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
if (cookieToken != null && formToken !=null)
{
AntiForgery.Validate(cookieToken, formToken);
}
else
{
AntiForgery.Validate();
}
}
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
}
}
}
In the below code IsAjaxRequest is an extension method
public static class HttpRequestMessageExtensions
{
public static bool IsAjaxRequest(this HttpRequestMessage request)
{
IEnumerable<string> headers;
if (request.Headers.TryGetValues("X-Requested-With", out headers))
{
var header = headers.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(header))
{
return header.ToLowerInvariant() == "xmlhttprequest";
}
}
return false;
}
}
Here my issue how to mock the IsAjaxRequest and how to pass actionContext parameter to the OnActionExecuting method.
Can anyone help me to provide some code samples regarding this?
IsAjaxRequest is an extension method which mean it is a static method.
You shouldn't mock static methods. You should test it as a part of your method under test behavior.
The following example shows how to test invalid request:(I removed the :: ErrorSignal.FromCurrentContext().Raise(ex); since I didn't know which assembly to add... so add the missing assert\s in your test...)
[TestMethod]
public void TestMethod1()
{
var target = new ValidateJsonAntiForgeryTokenAttribute();
var requestMessage = new HttpRequestMessage();
requestMessage.Headers.Add("X-Requested-With", new[] {"xmlhttprequest"});
var fakeDescriptor = new Mock<HttpActionDescriptor>();
var controllerContext = new HttpControllerContext {Request = requestMessage};
var context = new HttpActionContext(controllerContext, fakeDescriptor.Object);
target.OnActionExecuting(context);
Assert.AreEqual(HttpStatusCode.Forbidden, actionContext.Response.StatusCode);
}
You can mock the static method if you really need to. There's my example of the test with Typemock isolator:
[TestMethod, Isolated]
public void TestValidate()
{
//How to fake action context to further passing it as a parameter:
var fake = Isolate.Fake.AllInstances<HttpActionContext>();
Isolate.Fake.StaticMethods<HttpActionContext>();
//How to mock IsAjaxRequset:
var request = new HttpRequestMessage();
Isolate.WhenCalled(() => request.IsAjaxRequest()).WillReturn(true);
//Arrange:
ValidateJsonAntiForgeryTokenAttribute target = new ValidateJsonAntiForgeryTokenAttribute();
Isolate.WhenCalled(() => AntiForgery.Validate()).IgnoreCall();
//Act:
target.OnActionExecuting(fake);
//Assert:
Isolate.Verify.WasCalledWithAnyArguments(() => AntiForgery.Validate());
}
I'm trying to mock function like below:
AntiForgeryValidator a = new AntiForgeryValidator();
public void ValidateRequestHeader(HttpRequestBase request)
{
string cookieToken = "";
string formToken = "";
if (request.Headers["RequestVerificationToken"] != null)
{
string[] tokens = request.Headers["RequestVerificationToken"].Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
a.Validate(cookieToken, formToken);
//AntiForgery.Validate(cookieToken, formToken);
}
So I created interface:
public interface IAntiForgeryValidator
{
//void ValidateRequestHeader(HttpRequestBase request);
void Validate(string cookieToken, string formToken);
}
public class AntiForgeryValidator : IAntiForgeryValidator
{
public void Validate(string cookieToken, string formToken)
{
AntiForgery.Validate(cookieToken, formToken);
}
}
And add new code to the test project:
Mocking Http Request:
Mock<HttpRequestBase> Request = new Mock<HttpRequestBase>();
Request.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection{
{"RequestVerificationToken", "RequestVerificationToken"}
});
And mock validate method:
Mock<IAntiForgeryValidator> antiForgeryMock = new Mock<IAntiForgeryValidator>();
antiForgeryMock.Setup(m => m.Validate(
It.IsAny<string>(),
It.IsAny<string>()))
.Callback((string cookieToken, string formToken) =>
{
// call back
});
antiForgeryMock.Verify(m => m.Validate(
It.IsAny<string>(),
It.IsAny<string>()), Times.Once());
This is all code I managed to create using answer found on this page.
But I'm still getting error like in the topic.
I suppose that all its caused by this lines:
.Callback((string cookieToken, string formToken) =>
{
// call back
});
But I have no idea what should I put there. Reading Moq docs didn't help either.
#update:
Trying to done this like creating fakeRepository
private IAntiForgeryValidator fakeValidation;
fakeValidation = antiForgeryMock.Object;
Function when I'm trying to fire tests:
[TestMethod]
public void EditPost()
{
var data = default(Device);
var result = DC.Edit(data);
Assert.IsNotNull(result);
}
#Update two:
added new code:
DC.ControllerContext = new ControllerContext(context.Object,new RouteData(),DC);
Now If I comment out Veryfy code works and is passed to controller. But still not working uncommented
You have to put the antiForgeryMock.Verify() after you've run your test action. A test usually looks like this:
// arrange
var myMock = new Mock<MyInterface>();
myMock.Setup(...);
var mySut = new Sut(myMock.Object);
// act
mySut.DoSomething()
//assert
myMock.Verify(...);
As mentioned in the comments, you don't need a Setup for your test, so you will skip that part.
I have 2 concerns about my unit test method:
Do I test too much in one test method?
How can my test method name reflect all test expectations?
I asked myself when my method name says: ReturnInvalidModelState, then my 2 other Asserts are not correct. At least concerning the method name...
[Test]
public void Create_TemplateAlreadyExists_ReturnInvalidModelState()
{
// ARRANGE
TemplateViewModel templateViewModel = new TemplateViewModel {
Name = "MyTest"
};
Mock<ITemplateDataProvider> mock1 = new Mock<ITemplateDataProvider>();
Mock<IMappingEngine> mock2 = new Mock<IMappingEngine>();
TemplateController controller =
new TemplateController(mock1.Object, mock2.Object);
mock1.Setup(m => m.TemplateExists("MyTest")).Returns(true);
// Set ModelState.IsValid to false
controller.ModelState.AddModelError("Name",
"This name already exists.");
// ACT
ActionResult result = controller.Create(templateViewModel);
// ASSERT
Assert.IsFalse(controller.ModelState.IsValid);
Assert.IsInstanceOfType(typeof(PartialViewResult), result);
Assert.AreEqual(templateViewModel, ((PartialViewResult)result).Model);
}
[HttpPost]
public ActionResult Create(TemplateViewModel templateViewModel)
{
if (ModelState.IsValid
&& !_templateDataProvider.TemplateExists(templateViewModel.Name))
{
Template template =
Mapper.Map<TemplateViewModel, Template>(templateViewModel);
_templateDataProvider.AddTemplate(template);
return new JsonNetResult(new { success = true });
}
ModelState.AddModelError("Name", "This name already exists.");
return PartialView(templateViewModel);
}
Yes, I think that you are testing for too many things.
Start with renaming your test method. Your method signature should describe action, scenario and expected outcome.
If I were to rename your method, than I would end up with the following:
public void Create_DuplicateTemplate_ModelStateIsInvalidAndReturnsPartialViewResultAndPartialViewResultOfTypeTemplateViewModel()
{
}
Your test is concerned with three things, rather than one. When it fails, you won't know straight away why it has failed.
Consider re-factoring this into smaller tests and encapsulating some of the arrangement logic so that it can be re-used.
Edit:
You made a good point in your comment regarding single test method having a single assertion. I agree with you on that one, as good as it sounds, often it's not sufficient.
Say I have the following action method:
[HttpPost]
public ActionResult Register(NewUserViewModel newUser)
{
if (!ModelState.IsValid)
return View(newUser);
var newUserDTO = Mapper.Map<NewUserViewModel, NewUserDTO>(newUser);
var userDTO = UserManagementService.RegisterUser(newUserDTO);
var result = Mapper.Map<UserDTO, UserViewModel>(userDTO);
TempData.Add("RegisteredUser", result);
return RedirectToAction("RegisterSuccess");
}
I have the following unit test for this method:
[TestMethod]
public void Register_HttpPost_ValidViewModel_ReturnsRegisterSuccess()
{
// Arrange
var autoMapperMock = this.mockRepository.DynamicMock<IMapper>();
var userManagementServiceMock = this.mockRepository.DynamicMock<IUserManagementService>();
var invalidRegistrationViewModel = new NewUserViewModel
{
LastName = "Lastname",
FirstName = "Firstname",
Username = null
};
autoMapperMock.Expect(a => a.Map<UserDTO, UserViewModel>(Arg<UserDTO>.Is.Anything)).Repeat.Once().Return(null);
autoMapperMock.Expect(a => a.Map<NewUserViewModel, NewUserDTO>(Arg<NewUserViewModel>.Is.Anything)).Repeat.Once().Return(null);
userManagementServiceMock.Expect(s => s.RegisterUser(Arg<NewUserDTO>.Is.Anything)).Repeat.Once();
autoMapperMock.Replay();
var controller = new AccountController
{
Mapper = autoMapperMock,
UserManagementService = userManagementServiceMock
};
this.mockRepository.ReplayAll();
// Act
var result = (RedirectToRouteResult)controller.Register(invalidRegistrationViewModel);
// Assert
Assert.IsTrue((string)result.RouteValues["Action"] == "RegisterSuccess");
}
As you can see, I set up multiple expectations on my mock:
I expect AutoMapper to be called twice
I expect UserManagementService to be called once
At the end of the test I have a single assertion that checks whether user was re-directed to the correct route.
So where do I check my assertions? I create another method that makes sure that my expectations have been met:
[TestCleanup]
public void Cleanup()
{
try
{
this.mockRepository.VerifyAll();
}
finally
{
}
}
So you are right, I have three assertions instead of one, but I structure my code in such a way so it appears that I have only one assertion.
I would recomend moving all of the "Arrange" and "Act" code into a Setup() method, and split the rest into three tests. This will make each individual test much easier to read, and let you give each test a name that corresponds better to the actual assert it contains.
private TemplateViewModel _templateViewModel;
private ITemplateDataProvider _mock2;
private IMappingEngine _mock2;
private TemplateController _controller;
private ActionResult _result;
[Setup]
public void Setup(){
// ARRANGE
_templateViewModel = new TemplateViewModel { Name = "MyTest" };
_mock1 = new Mock<ITemplateDataProvider>();
_mock2 = new Mock<IMappingEngine>();
_controller = new TemplateController(_mock1.Object, _mock2.Object);
_mock1.Setup(m => m.TemplateExists("MyTest")).Returns(true);
// Set ModelState.IsValid to false
_controller.ModelState.AddModelError("Name",
"This name already exists.");
_result = controller.Create(_templateViewModel);
}
[Test]
public void Create_TemplateAlreadyExists_ModelStateIsInvalid()
{
Assert.IsFalse(_controller.ModelState.IsValid);
}
[Test]
public void Create_TemplateAlreadyExists_ResultIsPartialViewResult()
{
Assert.IsInstanceOfType(typeof(PartialViewResult), _result);
}
[Test]
public void Create_TemplateAlreadyExists_ResultModelMatchesTemplateModel()
{
Assert.AreEqual(_templateViewModel, ((PartialViewResult)_result).Model);
}