English 中文(简体)
与 Moq的未预期行为
原标题:Unexpected Validate behavior with Moq

我在最近的项目上把我推向了一步。 我最近升级为4.0.10827版,我对我看来是一种新行为表示怀疑。

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.

我试图在下文的法典中说明这一点(令人信服地澄清,这是这一进程中的一个轨道)。

  1. I first create a new TestClass object. Its Var property is set to "one".
  2. I then create the mocked object, mockedObject, which is my test subject.
  3. 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).
  4. 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.
  5. I then make a change to the original TestClass object by re-assigning the Var property to "two".
  6. 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.
  7. 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.

在我看来,似乎很显然,莫克只是在提及原始的<编码>试验目录<<>标的时,才允许我改变其价值而不受惩罚,对我的测试结果造成不利影响。

关于测试法的一些说明。 <代码>IMyMocked Interface是我模拟的接口。 http://www.un.org/Depts/DGACM/index_chinese.htm 最后,When_Releaseing是包含测试代码的实际测试类别。 它正在使用。 因此,框架有几个奇题(因为......,应当......)。 这些代表只是框架要求进行测试的代表。 如果有此需要,应易于消除这些障碍,并将其列入标准功能。 我以这种格式离开,是因为它允许所有<代码>Validate的电话(与《阿奇怪法》相比)填写完毕。 简言之,下面的法典不是我遇到问题的实际法典。 这只是为了说明问题,因为我已在多个地方看到了同样的行为。

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());
}

我就此提出了几个问题:

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
};
最佳回答

归根结底,你的问题是,模拟框架是否应当对你在与 mo合时所使用的参数进行清点,以便准确记录系统处于互动点的国家,而不是在核查点可能具备的参数。

我要说的是,从逻辑角度看,这是合理的期望。 你正在采取具有价值Y的行动十。 如果你要求模拟“我采取具有Y价值的X行动”,你会说“是”,而不管制度目前的状况如何。

1. 总结一下你正在讨论的问题:


  • 您首先就具有参考参数的模拟物体援引一种方法。

  • Moq 节省了有关援引的信息以及的参考参数。

  • 然后,你问莫克,该方法是否被称作一次,其标语与你通过的字面相同。

  • Moq checks its history for a call to that method with a parameter that matches the supplied parameter and answers yes.

  • 然后,你将你作为参数通过的标语改为 mo。

  • 参考文件Moq的记忆空间正在其历史上对新价值的改变。

  • 然后,你问莫克,该方法是否被称作是一度,其标语与它持有的字面相同。

  • Mock checks its history for a call to that method with a parameter that matches the supplied parameter and reports no.


2. 努力回答你的具体问题:

  1. 这一行为是否预期?

    I 表示没有

  2. 这一新行为吗?

    我不知道,但令人怀疑的是,该项目在某个时候会发生有利于这一目的的行为,后来又作了修改,只允许简单地核实每个模拟的单一使用。

  3. 是否有我不知道的工作?

    I ll 回答这两个方面:

    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.

    从测试角度看,我建议你遵循以下原则:。 我认为,有时间进行基于国家的测试和基于互动的测试,但除非互动是设想中的一个重要部分,否则,你应努力避免与执行细节发生混淆。 在某些情况下,你感兴趣的设想主要是互动(“账户间转拨资金”,但在其他情况下,你真正关心的是取得正确结果(“批拨10美元”)。 对于你的控制人员,这似乎属于问询类别,而不是指挥类别。 只要正确,你就不真正关心如何取得你想要的结果。 因此,我建议在这种情况下采用基于国家的测试方法。 如果另一个具体条款涉及对系统发出指挥,那么最终可能仍然是门前解决办法,你们应考虑首先使用,但可能需要或必须进行基于互动的测试。 我的想法是

  4. 我是否使用“正确”?

    You is using the Verification (() methods, it only doesn t support the Options You are used for.

  5. 是否有更好的办法利用Moq避免这种情况?

    I. 目前没有考虑Mq 处理这种情况。

希望这一帮助,

Derek Greer
http://derekgreer.lostechies.com
http://aspiringcraftsman.com
@derekgreer

问题回答

首先,你可以通过宣布避免<代码>Moq和MSpec之间的冲突。

using Machine.Specifications;
using Moq;
using It = Machine.Specifications.It;

然后,你只需要预先确定<代码>。 Moq. 当你想要使用Moq s时,见<>t>。


www.un.org/Depts/DGACM/index_spanish.htm 问题:。

<>说明: 这不是原来的答案,而是在《任择议定书》在问题上添加了某些真实的实例。

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:

  1. Because delegate is run
  2. 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.

我尝试用<代码>标示你的光谱。 Setup forEachSpecificationAttribute:

[Subject(typeof(object)), SetupForEachSpecification]
public class When_Testing
{
    // Something, Something, something... 
}

其名称是: 它将操作<条码>。 <代码>。 添加属性使光谱按预期发生:3项成功,1项失败(与Var=“2”的核查)。

<代码> 在每个<代码>后再设计。 它 无法为你的测试所接受?

FYI: I m using Moq v4.0.10827.0 and MSpec v0.4.9.<0/code>


Free tip #2: 如果你重新测试伙伴关系。 http://jamesbroo.me/introducing-nchnespecificationsmvc/"rel=“nofollow” James Broome sMSpec extension for MVC





相关问题
WebForms and ASP.NET MVC co-existence

I am trying to make a WebForms project and ASP.NET MVC per this question. One of the things I ve done to make that happen is that I added a namespaces node to the WebForms web.config: <pages ...

Post back complex object from client side

I m using ASP.NET MVC and Entity Framework. I m going to pass a complex entity to the client side and allow the user to modify it, and post it back to the controller. But I don t know how to do that ...

Create an incremental placeholder in NHaml

What I want to reach is a way to add a script and style placeholder in my master. They will include my initial site.css and jquery.js files. Each haml page or partial can then add their own required ...

asp.net mvc automapper parsing

let s say we have something like this public class Person { public string Name {get; set;} public Country Country {get; set;} } public class PersonViewModel { public Person Person {get; ...

structureMap mocks stub help

I have an BLL that does validation on user input then inserts a parent(PorEO) and then inserts children(PorBoxEO). So there are two calls to the same InsertJCDC. One like this=>InsertJCDC(fakePor)...

ASP.NET MVC: How should it work with subversion?

So, I have an asp.net mvc app that is being worked on by multiple developers in differing capacities. This is our first time working on a mvc app and my first time working with .NET. Our app does not ...

System.Web.Mvc.Controller Initialize

i have the following base controller... public class BaseController : Controller { protected override void Initialize(System.Web.Routing.RequestContext requestContext) { if (...