Moq has been driving me a bit crazy on my latest project. I recently upgraded to version 4.0.10827, and I'm noticing what seems to me to be a new behavior.
Basically, when I call my mocked function (MakeCall, in this example) in the code I am testing, I am passing in an object (TestClass). The code I am testing makes changes to the TestClass object before and after the call to MakeCall. Once the code has completed, I then call Moq's Verify function. My expectation is that Moq will have recorded the complete object that I passed into MakeCall, perhaps via a mechanism like deep cloning. This way, I will be able to verify that MakeCall was called with the exact object I am expecting it to be called with. Unfortunately, this is not what I'm seeing.
I attempt to illustrate this in the code below (hopefully, clarifying it a bit in the process).
I first create a new TestClass object. Its Var property is set to "one".
I then create the mocked object, mockedObject, which is my test subject.
I then call the MakeCall method of mockedObject (by the way, the Machine.Specifications framework used in the example allows the code in the When_Testing class to be read from top to bottom).
I then test the mocked object to ensure that it was indeed called with a TestClass with a Var value of "one". This succeeds, as I expected it to.
I then make a change to the original TestClass object by re-assigning the Var property to "two".
I then proceed to attempt to verify if Moq still thinks that MakeCall was called with a TestClass with a value of "one". This fails, although I am expecting it to be true.
Finally, I test to see if Moq thinks MakeCall was in fact called by a TestClass object with a value of "two". This succeeds, although I would initially have expected it to fail.
It seems pretty clear to me that Moq is only holding onto a reference to the original TestClass object, allowing me to change its value with impunity, adversely affecting the results of my testing.
A few notes on the test code. IMyMockedInterface is the interface I am mocking. TestClass is the class I am passing into the MakeCall method and therefore using to demonstrate the issue I am having. Finally, When_Testing is the actual test class that contains the test code. It is using the Machine.Specifications framework, which is why there are a few odd items ('Because of', 'It should...'). These are simply delegates that are called by the framework to execute the tests. They should be easily removed and the contained code placed into a standard function if that is desired. I left it in this format because it allows all Validate calls to complete (as compared to the 'Arrange, Act Assert' paradigm). Just to clarify, the below code is not the actual code I am having problems with. It is simply intended to illustrate the problem, as I have seen this same behavior in multiple places.
using Machine.Specifications;
// Moq has a conflict with MSpec as they both have an 'It' object.
using moq = Moq;
public interface IMyMockedInterface
{
int MakeCall(TestClass obj);
}
public class TestClass
{
public string Var { get; set; }
// Must override Equals so Moq treats two objects with the
// same value as equal (instead of comparing references).
public override bool Equals(object obj)
{
if ((obj != null) && (obj.GetType() != this.GetType()))
return false;
TestClass t = obj as TestClass;
if (t.Var != this.Var)
return false;
return true;
}
public override int GetHashCode()
{
int hash = 41;
int factor = 23;
hash = (hash ^ factor) * Var.GetHashCode();
return hash;
}
public override string ToString()
{
return MvcTemplateApp.Utilities.ClassEnhancementUtilities.ObjectToString(this);
}
}
[Subject(typeof(object))]
public class When_Testing
{
// TestClass is set up to contain a value of 'one'
protected static TestClass t = new TestClass() { Var = "one" };
protected static moq.Mock<IMyMockedInterface> mockedObject = new moq.Mock<IMyMockedInterface>();
Because of = () =>
{
mockedObject.Object.MakeCall(t);
};
// Test One
// Expected: Moq should verify that MakeCall was called with a TestClass with a value of 'one'.
// Actual: Moq does verify that MakeCall was called with a TestClass with a value of 'one'.
// Result: This is correct.
It should_verify_that_make_call_was_called_with_a_value_of_one = () =>
mockedObject.Verify(o => o.MakeCall(new TestClass() { Var = "one" }), moq.Times.Once());
// Update the original object to contain a new value.
It should_update_the_test_class_value_to_two = () =>
t.Var = "two";
// Test Two
// Expected: Moq should verify that MakeCall was called with a TestClass with a value of 'one'.
// Actual: The Verify call fails, claiming that MakeCall was never called with a TestClass instance with a value of 'one'.
// Result: This is incorrect.
It should_verify_that_make_call_was_called_with_a_class_containing_a_value_of_one = () =>
mockedObject.Verify(o => o.MakeCall(new TestClass() { Var = "one" }), moq.Times.Once());
// Test Three
// Expected: Moq should fail to verify that MakeCall was called with a TestClass with a value of 'two'.
// Actual: Moq actually does verify that MakeCall was called with a TestClass with a value of 'two'.
// Result: This is incorrect.
It should_fail_to_verify_that_make_call_was_called_with_a_class_containing_a_value_of_two = () =>
mockedObject.Verify(o => o.MakeCall(new TestClass() { Var = "two" }), moq.Times.Once());
}
I have a few questions regarding this:
Is this expected behavior?
Is this new behavior?
Is there a workaround that I am unaware of?
Am I using Verify incorrectly?
Is there a better way of using Moq to avoid this situation?
I thank you humbly for any assistance you can provide.
Edit:
Here is one of the actual tests and SUT code that I experienced this problem with. Hopefully it will act as clarification.
// This is the MVC Controller Action that I am testing. Note that it
// makes changes to the 'searchProjects' object before and after
// calling 'repository.SearchProjects'.
[HttpGet]
public ActionResult List(int? page, [Bind(Include = "Page, SearchType, SearchText, BeginDate, EndDate")]
SearchProjects searchProjects)
{
int itemCount;
searchProjects.ItemsPerPage = profile.ItemsPerPage;
searchProjects.Projects = repository.SearchProjects(searchProjects,
profile.UserKey, out itemCount);
searchProjects.TotalItems = itemCount;
return View(searchProjects);
}
// This is my test class for the controller's List action. The controller
// is instantiated in an Establish delegate in the 'with_project_controller'
// class, along with the SearchProjectsRequest, SearchProjectsRepositoryGet,
// and SearchProjectsResultGet objects which are defined below.
[Subject(typeof(ProjectController))]
public class When_the_project_list_method_is_called_via_a_get_request
: with_project_controller
{
protected static int itemCount;
protected static ViewResult result;
Because of = () =>
result = controller.List(s.Page, s.SearchProjectsRequest) as ViewResult;
// This test fails, as it is expecting the 'SearchProjects' object
// to contain:
// Page, SearchType, SearchText, BeginDate, EndDate and ItemsPerPage
It should_call_the_search_projects_repository_method = () =>
s.Repository.Verify(r => r.SearchProjects(s.SearchProjectsRepositoryGet,
s.UserKey, out itemCount), moq.Times.Once());
// This test succeeds, as it is expecting the 'SearchProjects' object
// to contain:
// Page, SearchType, SearchText, BeginDate, EndDate, ItemsPerPage,
// Projects and TotalItems
It should_call_the_search_projects_repository_method = () =>
s.Repository.Verify(r => r.SearchProjects(s.SearchProjectsResultGet,
s.UserKey, out itemCount), moq.Times.Once());
It should_return_the_correct_view_name = () =>
result.ViewName.ShouldBeEmpty();
It should_return_the_correct_view_model = () =>
result.Model.ShouldEqual(s.SearchProjectsResultGet);
}
/////////////////////////////////////////////////////
// Here are the values of the three test objects
/////////////////////////////////////////////////////
// This is the object that is returned by the client.
SearchProjects SearchProjectsRequest = new SearchProjects()
{
SearchType = SearchTypes.ProjectName,
SearchText = GetProjectRequest().Name,
Page = Page
};
// This is the object I am expecting the repository method to be called with.
SearchProjects SearchProjectsRepositoryGet = new SearchProjects()
{
SearchType = SearchTypes.ProjectName,
SearchText = GetProjectRequest().Name,
Page = Page,
ItemsPerPage = ItemsPerPage
};
// This is the complete object I expect to be returned to the view.
SearchProjects SearchProjectsResultGet = new SearchProjects()
{
SearchType = SearchTypes.ProjectName,
SearchText = GetProjectRequest().Name,
Page = Page,
ItemsPerPage = ItemsPerPage,
Projects = new List<Project>() { GetProjectRequest() },
TotalItems = TotalItems
};
Ultimately, your question is whether a mocking framework should take snapshots of the parameters you use when interacting with the mocks so that it can accurately record the state the system was in at the point of interaction rather than the state the parameters might be in at the point of verification.
I would say this is a reasonable expectation from a logical point of view. You are performing action X with value Y. If you ask the mock "Did I perform action X with value Y", you expect it to say "Yes" regardless of the current state of the system.
To summarize the problem you are running into:
You first invoke a method on a mock object with a reference type parameter.
Moq saves information about the invocation along with the reference type parameter passed in.
You then ask Moq if the method was called one time with an object equal to the reference you passed in.
Moq checks its history for a call to that method with a parameter that matches the supplied parameter and answers yes.
You then modify the object that you passed as the parameter to the method call on the mock.
The memory space of the reference Moq is holding in its history changes to the new value.
You then ask Moq if the method was called one time with an object that isn't equal to the reference its holding.
Mock checks its history for a call to that method with a parameter that matches the supplied parameter and reports no.
To attempt to answer your specific questions:
Is this expected behavior?
I would say no.
Is this new behavior?
I don't know, but it's doubtful the project would have at one time had behavior that facilitated this and was later modified to only allow the simple scenario of only verifying a single usage per mock.
Is there a workaround that I am unaware of?
I'll answer this two ways.
From a technical standpoint, a workaround would be to use a Test Spy rather than a Mock. By using a Test Spy, you can record the values passed and use your own strategy for remembering the state, such as doing a deep clone, serializing the object, or just storing the specific values you care about to be compared against later.
From a testing standpoint, I would recommend that you follow the principle "Use The Front Door First". I believe there is a time for state-based testing as well as interaction-based testing, but you should try to avoid coupling yourself to the implementation details unless the interaction is an important part of the scenario. In some cases, the scenario you are interested in will be primarily about interaction ("Transfer funds between accounts"), but in other cases all you really care about is getting the correct result ("Withdraw $10"). In the case of the specification for your controller, this seems to fall into the query category, not the command category. You don't really care how it gets the results you want as long as they are correct. Therefore, I would recommend using state-based testing in this case. If another specification concerns issuing a command against the system, there may still end up being a front door solution which you should consider using first, but it may be necessary or important to do interaction based testing. Just my thoughts though.
Am I using Verify incorrectly?
You are using the Verify() method correctly, it just doesn't support the scenario you are using it for.
Is there a better way of using Moq to avoid this situation?
I don't think Moq is currently implemented to handle this scenario.
Hope this helps,
Derek Greer
http://derekgreer.lostechies.com
http://aspiringcraftsman.com
#derekgreer
First, you can avoid the conflict between Moq and MSpec by declaring
using Machine.Specifications;
using Moq;
using It = Machine.Specifications.It;
Then you'll only need to prefix with Moq. when you want to use Moq's It, for example Moq.It.IsAny<>().
Onto your question.
Note: This is not the original answer but an edited one after the OP added some real example code to the question
I've been trying out your sample code and I think it's got more to do with MSpec than Moq. Apparently (and I didn't know this either), when you modify the state of your SUT (System Under Test) inside an It delegate the changes gets remembered. What is happening now is:
Because delegate is run
It delegates are run, one after the other. If one changes the state, the following It will never see the set up in the Because. Hence your failed test.
I've tried marking your spec with the SetupForEachSpecificationAttribute:
[Subject(typeof(object)), SetupForEachSpecification]
public class When_Testing
{
// Something, Something, something...
}
The attribute does as its name says: It will run your Establish and Because before every It. Adding the attribute made the spec behave as expected: 3 Successes, one fail (the verification that with Var = "two").
Would the SetupForEachSpecificationAttribute solve your problem or is resetting after every It not acceptable for your tests?
FYI: I'm using Moq v4.0.10827.0 and MSpec v0.4.9.0
Free tip #2: If you're testing ASP.NET MVC apps with Mspec you might want to take a look at James Broome's MSpec extensions for MVC
Related
I'm having a weird issue with the configureMetadataStore.
My model:
class SourceMaterial {
List<Job> Jobs {get; set;}
}
class Job {
public SourceMaterial SourceMaterial {get; set;}
}
class JobEditing : Job {}
class JobTranslation: Job {}
Module for configuring Job entities:
angular.module('cdt.request.model').factory('jobModel', ['breeze', 'dataService', 'entityService', modelFunc]);
function modelFunc(breeze, dataService, entityService) {
function Ctor() {
}
Ctor.extend = function (modelCtor) {
modelCtor.prototype = new Ctor();
modelCtor.prototype.constructor = modelCtor;
};
Ctor.prototype._configureMetadataStore = _configureMetadataStore;
return Ctor;
// constructor
function jobCtor() {
this.isScreenDeleted = null;
}
function _configureMetadataStore(entityName, metadataStore) {
metadataStore.registerEntityTypeCtor(entityName, jobCtor, jobInitializer);
}
function jobInitializer(job) { /* do stuff here */ }
}
Module for configuring JobEditing entities:
angular.module('cdt.request.model').factory(jobEditingModel, ['jobModel', modelFunc]);
function modelFunc(jobModel) {
function Ctor() {
this.configureMetadataStore = configureMetadataStore;
}
jobModel.extend(Ctor);
return Ctor;
function configureMetadataStore(metadataStore) {
return this._configureMetadataStore('JobEditing', metadataStore)
}
}
Module for configuring JobTranslation entities:
angular.module('cdt.request.model').factory(jobTranslationModel, ['jobModel', modelFunc]);
function modelFunc(jobModel) {
function Ctor() {
this.configureMetadataStore = configureMetadataStore;
}
jobModel.extend(Ctor);
return Ctor;
function configureMetadataStore(metadataStore) {
return this._configureMetadataStore('JobTranslation', metadataStore)
}
}
Then Models are configured like this :
JobEditingModel.configureMetadataStore(dataService.manager.metadataStore);
JobTranslationModel.configureMetadataStore(dataService.manager.metadataStore);
Now when I call createEntity for a JobEditing, the instance is created and at some point, breeze calls setNpValue and adds the newly created Job to the np SourceMaterial.
That's all fine, except that it is added twice !
It happens when rawAccessorFn(newValue); is called. In fact it is called twice.
And if I add a new type of job (hence I register a new type with the metadataStore), then the new Job is added three times to the np.
I can't see what I'm doing wrong. Can anyone help ?
EDIT
I've noticed that if I change:
metadataStore.registerEntityTypeCtor(entityName, jobCtor, jobInitializer);
to
metadataStore.registerEntityTypeCtor(entityName, null, jobInitializer);
Then everything works fine again ! So the problem is registering the same jobCtor function. Should that not be possible ?
Our Bad
Let's start with a Breeze bug, recently discovered, in the Breeze "backingStore" model library adapter.
There's a part of that adapter which is responsible for rewriting data properties of the entity constructor so that they become observable and self-validating and it kicks in when register a type with registerEntityTypeCtor.
It tries to keep track of which properties it has rewritten. The bug is that it records the fact of rewrite on the EntityType rather than on the constructor function. Consequently, every time you registered a new type, it failed to realize that it had already rewritten the properties of the base Job type and re-wrapped the property.
This was happening to you. Every derived type that you registered re-wrapped/re-wrote the properties of the base type (and of its base type, etc).
In your example, a base class Job property would be re-written 3 times and its inner logic executed 3 times if you registered three of its sub-types. And the problem disappeared when you stopped registering constructors of sub-types.
We're working on a revised Breeze "backingStore" model library adapter that won't have this problem and, coincidentally, will behave better in test scenarios (that's how we found the bug in the first place).
Your Bad?
Wow that's some hairy code you've got there. Why so complicated? In particular, why are you adding a one-time MetadataStore configuration to the prototypes of entity constructor functions?
I must be missing something. The code to register types is usually much smaller and simpler. I get that you want to put each type in its own file and have it self-register. The cost of that (as you've written it) is enormous bulk and complexity. Please reconsider your approach. Take a look at other Breeze samples, Zza-Node-Mongo for example.
Thanks for reporting the issue. Hang in there with us. A fix should be arriving soon ... I hope in the next release.
Setting up some integration tests, I'm having issues with domain class equality. The equality works as expected during normal execution, but when testing the Service methods through an integration test, the test for equality is coming back false.
One service (called in the setUp() of the Test Case) puts a Domain object into the session
SomeService {
setSessionVehicle(String name) {
Vehicle vehicle = Vehicle.findByName(name)
session.setAttribute("SessionVehicle", vehicle)
}
getSessionVehicle() {
return session.getAttribute("SessionVehicle")
}
}
Elsewhere in another service, I load an object and make sure the associated attribute object matches the session value:
OtherService {
getEngine(long id) {
Vehicle current = session.getAttribute("SessionVehicle")
Engine engine = Engine.get(id)
if(!engine.vehicle.equals(current)) throw Exception("Blah blah")
}
}
This works as expected during normal operation, preventing from loading the wrong engine (Ok, I sanitized the class names, pretend it makes sense). But in the integration test, that .equals() fails when it should succeed:
Vehicle testVehicle
setUp() {
Vehicle v = new Vehicle("Default")
v.save()
someService.setSessionVehicle("Default")
testVehicle = someService.getSessionVehicle()
}
testGetEngine() {
List<Engine> engines = Engine.findAllByVehicle(testVehicle)
//some assertions and checks
Engine e = otherService.getEngine(engines.get(0).id)
}
The findAll() call is correctly returning the list of all Engines associated with the vehicle in the session, but when I try to look up an individual Engine by ID, the equality check for session Vehicle vs Vehicle on the found Engine fails. Only a single vehicle has been created at this point and the Exception message displays that the session Vehicle and the Engine.Vehicle exist and are the same value.
If I attempt this equality check in the testCase itself, it fails, but I'm able to change the testCase to check if(vehicle.id == sessionVehicle.id) which succeeds, but I'm not keen on changing my production code in order to satisfy an integration test.
Am I doing something wrong when setting up these domain objects in my test case that I should be doing differently?
First of all, the equality check you are doing is just checking the reference. You should not use the default equals method for your check, better override the equals method in domain class.
There are two ways you can override the equals method:
1) you can use your IDE to auto-generate code for equals method(a lot of null checking etc..).
2) Preferred way: You can use EqualsBuilder and HashCodeBuilder classes from the Apache Commons project. The library should be already available to your application, or download the JAR file and place in lib. Here is sample code for using EqualsBuilder:
boolean equals(o) {
if ( !(o instanceof Vehicle) ) {
return false
}
def eb = new EqualsBuilder()
eb.append(id, o.id)
eb.append(name, o.name)
eb.append(otherProperties, o.otherProperties)
....
return eb.isEquals()
}
Another point is: how you are getting session in service? from RequestContextHolder? Its a good practice to not access session directly from service, rather send the value as method parameter in the service.
I have a unit test method that needs to mock (stub?) two repository method calls in the class that I'm testing. Every example I've worked through until now shows one setup method for the Mock, but now I need two.
Example:
_employeeRepositoryMock.Setup(e => e.GetEmployees())
.Returns(new Employee[]
{
new Employee
{
Name = "John Doe"
}
});
_employeeRepositoryMock.Setup(e => e.UpdateEmployee(1)).Returns(true);
Assert.IsTrue(_employeeService.UpdateEmployeeRecords() > 0);
_employeeRepositoryMock.Verify(gr => gr.UpdateEmployee(1), Times.Exactly(1));
In this example I need to mock two repository methods that are both called in "UpdateEmployeeRecords()" but I'm not sure how.
Update
Scratch this entire question -- I overlooked something simple. I was passing in the wrong numerical value for UpdateEmployee which was causing the Assert to fail. I changed the parameter in the mock to It.IsAny instead to get it to pass.
You can do it this way by creating the type of data the method should return (in my case a List<int> and a List<string>) and return it using the .Returns. Now whenever the DoSomething() method is called it will return my intResult List as the mocked data and the stringResult List when the DoSomethingElseThatIsReallyCool() method is called:
//Test method
{
List<int> intResult = new List<int>();
intResult.Add(0);
List<string> stringResult = new List<string>();
stringResult.Add("test");
_reposMock.Setup(r=>r.DoSomething()).Returns(intResult);
_reposMock.Setup(r=>r.DoSomethingElseThatIsReallyCool()).Returns(stringResult);
Assert.IsTrue(_reposMock.SomeMethod() > 0);
}
Your setup methods appear to be fine. Your Assertion must be failing for some other reason. Some thoughts:
Have you tried debugging your test to see what's going on?
Is 1 the correct value to be mocking in your UpdateEmployee method?
(Pardon me if this is patronizing, but I've done this before) Are you passing your mock to your service via _employeeRepositoryMock.Object?
I'm new to MSpec and would like to know if the way I wrote my test for ASP.NET MVC is correct. The test passes but I don't really like the way it's written and it seems awkward. I'm certainly missing something.
public class AccountControllerTests3
{
protected static AccountController controller;
static IFormsAuthenticationService formsService;
static IMembershipService membershipService;
protected static ActionResult result;
protected static LogOnModel model;
Establish context = () =>
{
var controllerBuilder = new TestControllerBuilder();
formsService = MockRepository.GenerateStub<IFormsAuthenticationService>();
membershipService = MockRepository.GenerateStub<IMembershipService>();
model = MockRepository.GenerateStub<LogOnModel>();
controller =
controllerBuilder.CreateController<AccountController>(new object[]
{
formsService,
membershipService
});
};
Because user_logs = () =>
{
bool rememberMe = false;
membershipService.Stub(
x => x.ValidateUser("bdd", "mspec")).Return(true);
formsService.Stub(x => x.SignIn("bdd", rememberMe));
controller.ModelState.IsValid.ShouldBeTrue();
};
}
[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_good_login_and_password : AccountControllerTests3
{
private It user_should_be_redirected_to_the_home_page = () =>
{
model.UserName = "bdd";
model.Password = "mspec";
result = controller.LogOn(model, string.Empty);
result.AssertActionRedirect().ToAction<HomeController>(
x => x.Index());
};
}
[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_bad_login_and_password : AccountControllerTests3
{
It The_error_message_should_be_shown = () =>
{
model.UserName = "BAD";
model.Password = "BAD";
result = controller.LogOn(model, string.Empty);
controller.ModelState[""].Errors[0].ErrorMessage.ShouldEqual(
"The user name or password provided is incorrect.");
};
}
Thanks in advance,
Thomas
One of my goals when I write tests with MSpec is to get the "Assert" or the "It" down to one line. MSpec is not like NUnit in that it only executes the Context (made up of the Establish clauses from the current class and all base classes and the Because clause) once followed by all of the Specifications (It clauses).
This is designed explicitly to force you to not perform any behavior in the It clauses, but rather observe it.
What you're actually doing here is using MSpec like NUnit. Try and rewrite the tests in a single class (using no inheritance). Work backwards... in the It, place a single line that asserts what you want to assert. Perhaps the AssertRedirect. In the Because, try and put a single line that causes the observations to be observable. This would probably be your call to the controller's logon method. Finally, in the "Establish context" you'd want to put everything else.
After a while, you may want to pull some of the things in the Establish context only into a base class, but in doing so, be sure that your entire subclass stands alone in terms of understanding. A reader shouldn't need to read the base class in order to understand what the actual spec is doing. It's ok to hide ceremonial implementation details, but be sure to hide them behind descriptive method names.
There's another line I'm not sure about:
controller.ModelState.IsValid.ShouldBeTrue();
If this is a test, it should probably be in its own It clause. Though really, do you want to test this? What are you testing here? Shouldn't your controller take an action based on whether or not the model is valid? Shouldn't the result of that action be observable (validation error instead of login error). I just wonder if you really need to test this.
A few other things to check out, first for styling with R#, it seems your tests are falling victim to R#'s defaults. I posted about how to fight this here:
http://codebetter.com/blogs/aaron.jensen/archive/2008/10/19/getting-resharper-and-vs-to-play-nice-with-mspec.aspx
Also, James Broome has some nice MVC MSpec extensions that are worth checking out:
http://jamesbroo.me/introducing-machinespecificationsmvc/
Good luck and Enjoy! Feel free to ping me on twitter if you have any other unrelated questions.
Here's a remark: instead of using CreateController method use InitializeController, because it is compile-time safer and refactor friendlier.
Instead of:
controller = controllerBuilder.CreateController<AccountController>(
new object[] { formsService, membershipService });
Do:
controller = new AccountController(formsService, membershipService);
controllerBuilder.InitializeController(controller);
The first will still compile if you change the controller constructor arguments and it will blow at runtime, while the second will generate a compile-time error.
i have a method that does takes in a object and saves it to the database. But, before i save the object, i do the following...
(psuedo code)
if (IsAuthenticated)
{
foo.UserId = AuthenticatedUser.Id;
}
else
{
foo.AnonEmail = "Jon#World-Domination";
foo.AnonName = "Jon Skeet";
}
try
{
_fooService.Save(foo);
}
catch
{
// Some view, with error stuff now added to
return View(...); ViewData.ModelState.
}
// all good, redirect to the proper next view.
return RedirectToAction(...);
That code works fine, but i'm not sure how to write the two unit tests for a success.
a) User is authenticated with valid data
b) User is not authentiated with valid data.
The reason why i'm not sure what to do is, is that both scenario return the same RedirectToAction(..) view object. So i can successfully test that .. but it doesn't tell me if the object saved contains the authenticated user id or the anon info. It's like i want the first unit test to say
moq up an authenticated user
call method
test if result is RedirectToActionView
test if the foo object that was persisted contains the moq'd user id.
thoughts?
Update
The common suggestion is that i mock the fooService. I'm currently using Dependency Injection and Moq, so could somone show me how i would use Moq? I'm not sure how the DI is important here, though ???
I would mock up the _fooService object, and test what it receives as part of your test. That way your surrounding code remains the same and is untouched, and by checking what _fooService receives, you can assert whether the behaviour is as expected. The return object is not of interest in this case.
How do you mock your _fooService ? You can either implement your own 'test' version (adhering to the same interface as the real world version), or using a mocking framework. Whichever approach you use, your code above needs to be configured with a given implementation of the _fooService (usually on construction - see dependency injection for more info on how this may work)
You might mock _fooService.Save(foo) and inspect the supplied foo.
Maybe you are finding it difficult to test the object because you have more than one activity taking place in a single method.
The overall theme here is controller logic.
Decorate the domain object with user information
Persist the update logic
Determine the next view to render based on success/failure
If you extract another object (IUserDecoratorService) then your code looks like
userService.UpdateUserInformation(foo);
try
{
_fooService.Save(foo);
}
catch
{
// Some view, with error stuff now added to
return View(...); ViewData.ModelState.
}
// all good, redirect to the proper next view.
return RedirectToAction(...);
This method is simple to test as it is 2 simple interactions with the 2 services and a routing decision which you can already test.
Now you just need to write the tests for your new service:
[Test]
public void ShouldDecorateWithUserIdForAuthenticatedUser()
{
{setup authenticated user}
:
service.UpdateUserInformation(foo);
Assert.AreEqual(expectedId, foo.UserId);
Assert.IsNull(foo.AnonEmail);
Assert.IsNull(foo.AnonEName);
}
[Test]
public void ShouldSpoofTheAllKnowingSkeetIfAnonymousUser()
{
{setup anonymous user}
:
service.UpdateUserInformation(foo);
Assert.AreEqual(UnassignedId, foo.UserId);
Assert.AreEqual("Jon#World-Domination", foo.AnonEmail);
Assert.AreEqual("Jon Skeet", foo.AnonName);
}
you still have a reference to your object. At the end of your unit test, couldn't you just assert what the value was foo.AnonEmail or UserId?
Unit tests should not touch an external source, (that's an integration test) so if you go that route you should mock your datasource and then test through your mock.
Do you have access to foo in the test? I'm assuming it's a field on the class. Is there a public getter? If not you may have to use Reflection (I assume that's available in asp.net, but I'm not too familiar with it)
You want to use the DI to get the correct implementation of fooservice into your object, so at testing time you can do this;
(Using Moq)
[TestMethod]
public void Jon_Skeet_Is_Saved_If_User_Not_Authenticated()
{
bool jonWasSaved = false;
var mockFooService = new Mock<IFooService>();
mockFooService
.Expect(x => x.Save(It.Is<Foo>(foo => foo.AnonName == "Jon Skeet")))
.Callback(() => jonWasSaved = true;);
FooManager instance = new FooManager(mockFooService.Object);
Foo testFoo = new Foo();
testFoo.UserId = 1; // this is not the authenticated id
instance.baa(foo);
Assert.IsTrue(jonWasSaved);
}
You may also want to pass in a mock version of whatever service you use to check the AuthetnicatedUser.Id
HTH