Note that there are some explanatory texts on larger screens.

plurals
  1. POUnexpected Validate behavior with Moq
    primarykey
    data
    text
    <p>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. </p> <p>Basically, when I call my mocked function (<code>MakeCall</code>, in this example) in the code I am testing, I am passing in an object (<code>TestClass</code>). The code I am testing makes changes to the <code>TestClass</code> object before and after the call to <code>MakeCall</code>. Once the code has completed, I then call Moq's <code>Verify</code> function. My expectation is that Moq will have recorded the complete object that I passed into <code>MakeCall</code>, perhaps via a mechanism like deep cloning. This way, I will be able to verify that <code>MakeCall</code> was called with the exact object I am expecting it to be called with. Unfortunately, this is not what I'm seeing. </p> <p>I attempt to illustrate this in the code below (hopefully, clarifying it a bit in the process). </p> <ol> <li>I first create a new <code>TestClass</code> object. Its <code>Var</code> property is set to <code>"one"</code>. </li> <li>I then create the mocked object, <code>mockedObject</code>, which is my test subject. </li> <li>I then call the <code>MakeCall</code> method of <code>mockedObject</code> (by the way, the Machine.Specifications framework used in the example allows the code in the <code>When_Testing</code> class to be read from top to bottom). </li> <li>I then test the mocked object to ensure that it was indeed called with a <code>TestClass</code> with a <code>Var</code> value of <code>"one"</code>. This succeeds, as I expected it to. </li> <li>I then make a change to the original <code>TestClass</code> object by re-assigning the <code>Var</code> property to <code>"two"</code>. </li> <li>I then proceed to attempt to verify if Moq still thinks that <code>MakeCall</code> was called with a <code>TestClass</code> with a value of <code>"one"</code>. This fails, although I am expecting it to be true. </li> <li>Finally, I test to see if Moq thinks <code>MakeCall</code> was in fact called by a <code>TestClass</code> object with a value of <code>"two"</code>. This succeeds, although I would initially have expected it to fail. </li> </ol> <p>It seems pretty clear to me that Moq is only holding onto a reference to the original <code>TestClass</code> object, allowing me to change its value with impunity, adversely affecting the results of my testing. </p> <p>A few notes on the test code. <code>IMyMockedInterface</code> is the interface I am mocking. <code>TestClass</code> is the class I am passing into the <code>MakeCall</code> method and therefore using to demonstrate the issue I am having. Finally, <code>When_Testing</code> is the actual test class that contains the test code. It is using the <a href="https://github.com/machine/machine.specifications" rel="noreferrer">Machine.Specifications</a> 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 <code>Validate</code> 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. </p> <pre><code>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) &amp;&amp; (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&lt;IMyMockedInterface&gt; mockedObject = new moq.Mock&lt;IMyMockedInterface&gt;(); Because of = () =&gt; { 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 = () =&gt; mockedObject.Verify(o =&gt; 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 = () =&gt; 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 = () =&gt; mockedObject.Verify(o =&gt; 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 = () =&gt; mockedObject.Verify(o =&gt; o.MakeCall(new TestClass() { Var = "two" }), moq.Times.Once()); } </code></pre> <p>I have a few questions regarding this:</p> <p>Is this expected behavior?<br> Is this new behavior?<br> Is there a workaround that I am unaware of?<br> Am I using Verify incorrectly?<br> Is there a better way of using Moq to avoid this situation? </p> <p>I thank you humbly for any assistance you can provide. </p> <p><strong>Edit:</strong><br> Here is one of the actual tests and SUT code that I experienced this problem with. Hopefully it will act as clarification. </p> <pre><code>// 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 = () =&gt; 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 = () =&gt; s.Repository.Verify(r =&gt; 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 = () =&gt; s.Repository.Verify(r =&gt; r.SearchProjects(s.SearchProjectsResultGet, s.UserKey, out itemCount), moq.Times.Once()); It should_return_the_correct_view_name = () =&gt; result.ViewName.ShouldBeEmpty(); It should_return_the_correct_view_model = () =&gt; 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&lt;Project&gt;() { GetProjectRequest() }, TotalItems = TotalItems }; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload