Moq out parameters - unit-testing

I'm fairly new to using Moq and Nunit for unit testing and I'm having issues with one scenario. What I want is for my mock to have an out parameters which my system under test will then use to decide what action to take.
My system under test is an MVC API controller and in particular I'm trying to test the POST method. I want to return an error message for the object when validation fails.
Here is the method code for the controller:
public IHttpActionResult Post(Candidate candidate)
{
try
{
if(candidate==null)
return BadRequest();
IEnumerable<string> errors;
_candidateManager.InsertCandidate(candidate, out errors);
if (errors!=null && errors.Any())
return BadRequest(CreateErrorMessage("Invalid candidate: ", errors));
return CreatedAtRoute("DefaultApi", new {id = candidate.CandidateId}, candidate);
}
catch (Exception)
{
return InternalServerError();
}
}
This is my Unit Test Code:
[Test]
[Category("CandidateManagerController Unit Tests")]
public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
{
IEnumerable<string> errors = new List<string>() {"error1", "error2"};
var mockManager = new Mock<ICandidateManager>();
mockManager.Setup(x => x.InsertCandidate(new Candidate(), out errors)).Callback(()=>GetErrors(errors));
var sut = new CandidateManagerController(mockManager.Object);
var actionResult = sut.Post(new Candidate());
Assert.IsInstanceOf<BadRequestResult>(actionResult);
}
What I expect is that when _candidateManager.InsertCandidate() is run then the errors variable is populated. However what is happening is that when you step through the controller code errors is null after _candidateManager.InsertCandidate() method is run.
If anyone has any ideas what I'm doing wrong or if what I want to do is not possible using Moq then please let me know.
Thanks

What you want to do is possible. If you look at the Quickstart docs at https://github.com/Moq/moq4/wiki/Quickstart, there is a section where it shows how you do setups for methods using out params. I have made two corrections to your code and it works.
You have to use the same candidate instance for both the mock setup and when you exercise the sut. Otherwise, Moq thinks that the two objects are different and your test setup becomes useless.
You don't have to use Callback in order to set the errors returned by the mocked CandidateManager.
Below is your test method with my changes.
[Test]
[Category("CandidateManagerController Unit Tests")]
public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
{
IEnumerable<string> errors = new List<string>() {"error1", "error2"};
//instance to be used for both setup and test later
var candidate = new Candidate();
var mockManager = new Mock<ICandidateManager>();
//removed Callback
mockManager.Setup(x => x.InsertCandidate(candidate, out errors));
var sut = new CandidateManagerController(mockManager.Object);
var actionResult = sut.Post(candidate);
Assert.IsInstanceOf<BadRequestResult>(actionResult);
}

You have to make sure that when you call your SUT that you use the same instance passed to the out argument otherwise the call will fail.
In your example, the method under test passes a null instance into the mocked method thus negating the setup of the test.
If however you are not able to supply the same instances for the out then it doesn't look like you will be able to get a mock to pass successfully. Take a look a the Quick Start for Moq to get an understanding of it capabilities.

Related

Navigation Unit Testing in MvvmCross

Trying to unit test the navigation in one of my command calls into a private method. Just trying to test if the navigation request has been made as a result of this command execution.
There's the old documentation;
https://www.mvvmcross.com/documentation/fundamentals/testing
This documentation does not factor in new async based calls as far as I found; For example IMvxMainThreadAsyncDispatcher
Either we need to implement two ExecuteOnMainThreadAsync methods or inherit from MvxMainThreadAsyncDispatcher in MockDispatcher.
Also need to add IMvxMainThreadAsyncDispatcher in IoC registration.
var mockDispatcher = new MockDispatcher();
...
...
Ioc.RegisterSingleton<IMvxMainThreadAsyncDispatcher>(MockDispatcher);
So almost all tests work except navigation call requests. Below method inside MockDispatcher never gets called so I can't check request counts;
public async Task<bool> ShowViewModel(MvxViewModelRequest request)
{
Requests.Add(request);
return true;
}
Anybody has a working code that would mock and gets this Request called or in some other form? IMvxMainThreadDispatcher is being set as absolute, and navigation calls are not done with ShowViewModel<>() anymore in MVVMCross, it's done by calling navigationService.Navigate();
Well, I have found the solution to my question... The ShowViewModel is called when navigation service is properly mocked. I have found a piece of code on GitHub from MvvmCross's own repo on how they do tests for navigation. My next challenge would be to mock the destination viewModel but that's separate and below code doesn't cover that. Previously I had a very basic IMvxNavigationService mock.
var mockLocator = new Mock<IMvxViewModelLocator>();
mockLocator.Setup(
m => m.Load(It.IsAny<Type>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxNavigateEventArgs>())).Returns(() => new FakeViewModel());
mockLocator.Setup(
m => m.Reload(It.IsAny<IMvxViewModel>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxNavigateEventArgs>())).Returns(() => new FakeViewModel());
var mockCollection = new Mock<IMvxViewModelLocatorCollection>();
mockCollection.Setup(m => m.FindViewModelLocator(It.IsAny<MvxViewModelRequest>()))
.Returns(() => mockLocator.Object);
Ioc.RegisterSingleton(mockLocator.Object);
var loader = new MvxViewModelLoader(mockCollection.Object);
_navigationService = new MvxNavigationService(null, loader)
{
ViewDispatcher = MockDispatcher,
};
_navigationService.LoadRoutes(new[] { typeof(YourViewModelTestClass).Assembly });
Ioc.RegisterSingleton<IMvxNavigationService>(_navigationService);

Unit testing not using correct mocking class

I am using Rhino.Mocks and Structure map to help unit test my code. I have several tests that pass when they are ran by themselves, but when ran as a group fail to pass. The setup code for these unit tests is:
[TestInitialize()]
public void Setup()
{
ObjectFactory.Initialize(x =>
{
x.For(IManager)().Use(Handler)();
});
}
In my tests, I stub out this interface and call the method.
[TestMethod]
public void AreMultiple_Test()
{
var mackIManager = MockRepository.GenerateMock<IManager>();
mackIManager.Stub(u => u.GetTwoUserName(Arg<int>.Is.Anything)).Return(null);
ObjectFactory.Inject(typeof(IManager), mackIManager);
StepAdditionalActionBase actionBase = new StepAdditionalActionBase();
bool areMultiple = actionBase.AreMultiple(new WorkOrder { Id = "123" });
Assert.IsFalse(areMultiple);
}
Test Method 2
[TestMethod]
public void AreMultiple_Test()
{
var mackIManager = MockRepository.GenerateMock<IManager>();
mackIManager.Stub(u => u.GetTwoUserName(Arg<int>.Is.Anything)).Return("123");
ObjectFactory.Inject(typeof(IManager), mackIManager);
StepAdditionalActionBase actionBase = new StepAdditionalActionBase();
bool areMultiple = actionBase.AreMultiple(new WorkOrder { Id = "123" });
Assert.IsTrue(areMultiple);
}
This is unit testing the following code.
public bool AreMultiple(WorkOrder workOrder)
{
string secondUser = _handler.GetTwoUserName(_workflowManager.GetNumberForProject(workOrder.Id));
if (String.IsNullOrEmpty(secondUser ))
{
return false;
}
return true;
}
When I run them by themselves, they work fine. When I run them together, the first passes and the second fails. When I debug the second one, I find that that the return value in the Stubbed method is still coming back as null. How do I get this to use the new Stubbed method.
UPDATE.
I am using StructureMap as my container. From what I have been able to find, the following code is what is used to dispose of the container I got it from this link. When I added this, the test still fail when ran together, but pass when ran individually.
[TestCleanup()]
public void TestCLeanup()
{
ObjectFactory.Container.Dispose();
}
The tests work one by one but fails if run all together. The problem should be in the common part which is being shared across the tests making them dependent from each other. In this particular case that is static ObjectFactory which is nothing else but a Service Locator (anti-pattern).
In the tests, you mock the IManager interface and register it in the ObjectFactory:
ObjectFactory.Inject(typeof(IManager), mackIManager);
Then the SUT uses the ObjectFactory service locator to resolve and use the mocked interface (_handler field):
string secondUser = _handler.GetTwoUserName(...)
I suspect the first test registers the _handler and never clean it up properly, so that the same instance appears in the second test. You should reset the ObjectFactory between tests following the Register Resolve Release pattern.
Another (preferable) option is to refactor your SUT to receive the IManager handler dependency explicitly via constructor. That would simplify both SUT and tests moving the ObjectFactory configuration to the Composition Root.

Issue testing Laravel Controller with Mockery | trying to get property of non-object

I'm very new to testing controllers and I'm running into a problem with a method(). I believe I'm either missing something in my test or my Controller / Repository is designed incorrectly.
The application I'm writing is basically one of those secure "one time" tools. Where you create a note, the system provides you with a URL, once that url is retrieved the note is deleted. I actually have the application written but I am going back to write tests for practice (I know that's backwards).
My Controller:
use OneTimeNote\Repositories\NoteRepositoryInterface as Note;
class NoteController extends \Controller {
protected $note;
public function __construct(Note $note)
{
$this->note = $note;
}
public function getNote($url_id, $key)
{
$note = $this->note->find($url_id, $key);
if (!$note) {
return \Response::json(array('message' => 'Note not found'), 404);
}
$this->note->delete($note->id);
return \Response::json($note);
}
...
I've injected my Note interface in to my controller and all is well.
My Test
use \Mockery as M;
class OneTimeNoteTest extends TestCase {
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('OneTimeNote\Repositories\EloquentNoteRepository');
}
public function mock($class)
{
$mock = M::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testShouldReturnNoteObj()
{
// Should Return Note
$this->mock->shouldReceive('find')->once()->andReturn('test');
$note = $this->call('GET', '/note/1234567890abcdefg/1234567890abcdefg');
$this->assertEquals('test', $note->getContent());
}
}
...
The error I'm getting
1) OneTimeNoteTest::testShouldReturnNoteObj
ErrorException: Trying to get property of non-object
/Users/andrew/laravel/app/OneTimeNote/Controllers/NoteController.php:24
Line 24 is in reference to this line found in my controller:
$this->note->delete($note->id);
Basically my abstracted repository method delete() obviously can't find $note->id because it really doesn't exist in the testing environment. Should I create a Note within the test and try to actually deleting it? Or would that be something that should be a model test? As you can see I need help, thanks!
----- Update -----
I tried to stub the repository to return a Note object as Dave Marshall mentioned in his answer, however I'm now receiving another error.
1) OneTimeNoteTest::testShouldReturnNoteObj
BadMethodCallException: Method Mockery_0_OneTimeNote_Repositories_EloquentNoteRepository::delete() does not exist on this mock object
I do have a delete() method in my repository and I know it's working when I test my route in the browser.
public function delete($id)
{
Note::find($id)->delete();
}
You are stubbing the note repository to return a string, PHP is then trying to retrieve the id attribute of a string, hence the error.
You should stub the repository to return a Note object, something like:
$this->mock->shouldReceive('find')->once()->andReturn(new Note());
Building upon Dave's answer, I was able to figure out what my problem is. I wasn't mocking the delete() method. I didn't understand the need to mock each individual method in my controller that would be called.
I just added this line:
$mock->shouldReceive('delete')->once()->andReturnNull();
Since my delete method is just deleting the note after it is found, I went ahead and mocked it but set it to return null.

First Unit Tests! ASP.NET MVC with Repositories causing errors

I am very new to Unit Testing, so I am starting on my first set of tests today. I am using the Library JustMock from Telerik. Though any unit testing information is good. I am having a bit of trouble with an interface service that passes through my method. Below is my MembershipController.Register(model) method...
[CaptchaValidator]
[HttpPost]
public ActionResult Register(Models.Membership.Registration model)
{
// just for good mesure, truncate any spaces that are given
System.Text.RegularExpressions.Regex.Replace(model.Email, #"\s", "");
if (ModelState.IsValid)
{
// Attempt to register the User and return any applicable status that has to do
// with the result.
var createStatus = membershipService.RegisterMember(model.Email, model.Password);
// if the member is able to be created successfully, log them in now and begin the
// authentication portion of the registration, otherwise, display the registration
// errors and return to the view.
if (createStatus == Membership.MemberCreateStatus.Success)
{
formsAuthentication.SignIn(model.Email, false /* createPersistentCookie */);
return RedirectToAction("Success");
}
else
{
ModelState.AddModelError("", Membership.Validator.ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
And here is the paltry test I am trying to run...
[TestMethod]
public void Register_Post_ReturnsRedirectOnSuccess()
{
// Arrange
var controller = Mock.Create<Web.Controllers.MembershipController>();
var repository = Mock.Create<Membership.IMembershipService>();
Mock.Arrange(() => repository.RegisterMember("acceptible#email.com", "acceptiblePassword")).Returns(Membership.MemberCreateStatus.Success);
// Model
var model = new Web.Models.Membership.Registration
{
Email = "acceptible#email.com",
Password = "acceptiblePassword",
ConfirmPassword = "acceptiblePassword"
};
// Act
var result = controller.Register(model);
// Assert
Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
}
The test fails because membershipService is resolving as null. I'm not sure what to do here. This is my first forray into the Unit Testing aspect of ASP.NET MVC. Can anyone give me some advice?
I am using Ninject to inject IMembershipService through the Constructor. It is implemented by the class MembershipService. The code runs fine when I run it, but the unit tests fail.
I don't see you passing repository anywhere into your controller. Normally you would have IMembershipService as a parameter in your controller's constructor that you can then pass in when needed or use MVC's Service Locator to grab the Ninject instance and pass it in.
:)

How to test binders/property editors used on spring 2.5 controllers

I have an annotated controller with a method that expects a model and a binding result
#RequestMapping(method = RequestMethod.POST)
public ModelAndView submit(#ModelAttribute(“user”) User user, BindingResult bindingResult) {
//do something
}
How do I test the binding result? If I call the method with a user and a binding result then I'm not testing the binding process. I figure there myst be something that takes a MockHttpServletRequest and returns the model and the binding result, any suggestions?
Are you trying to test the binding (which happens before this method would be called) or are you trying to test the "submit" handler method?
You can test the binding with something like this:
#Test
public void testHandlerMethod() {
final MockHttpServletRequest request = new MockHttpServletRequest("post", "/...");
request.setParameter("firstName", "Joe");
request.setParameter("lastName", "Smith");
final User user = new User();
final WebDataBinder binder = new WebDataBinder(user, "user");
binder.bind(new MutablePropertyValues(request.getParameterMap()));
final ModelAndView mv = controllerTestInstance.submit(user, binder.getBindingResult());
// Asserts...
}
You will probably find the spring-test-mvc project very relevant to what you're trying to achieve. The project is usable today and is quite simple overall but there will be some changes along the way, so if you need a stable API then this isn't for you just yet.