【问题标题】:How do I AssertWasCalled a generic method with three different types using RhinoMocks?如何使用 RhinoMocks 断言具有三种不同类型的泛型方法?
【发布时间】:2010-11-19 16:00:04
【问题描述】:

我正在尝试学习 Rhino Mocks AAA 语法,但在断言某个方法(带有任何参数值)被调用时遇到问题。我使用 Machine.Specifications 作为我的测试框架。

这个特殊的方法是通用的,我想确保它被三种不同的类型调用了三次。

repo.Save<T1>(anything), repo.Save<T2>(anything), and repo.Save<T3>(anything)

我为每种类型的函数存根。但我得到了一个有趣的结果。 (下)

[Subject("Test")]
public class When_something_happens_with_constraint
{
    static IRepository repo;
    static TestController controller;
    static ActionResult result;

    Establish context = () =>
    {
        repo = MockRepository.GenerateMock<IRepository>();
        controller = new TestController(repo);
        repo.Stub(o => o.Save<Something>(Arg<Something>.Is.Anything));
        repo.Stub(o => o.Save<SomethingElse>(Arg<SomethingElse>.Is.Anything));
        repo.Stub(o => o.Save<AnotherOne>(Arg<AnotherOne>.Is.Anything));
    };

    //post data to a controller
    Because of = () => { result = controller.SaveAction(new SomethingModel() { Name = "test", Description = "test" }); };

    //controller constructs its own something using the data posted, then saves it. I want to make sure three calls were made.  
    It Should_save_something = () => repo.AssertWasCalled(o => o.Save<Somethign>(Arg<Something>.Is.Anything));
    It Should_save_something_else = () => repo.AssertWasCalled(o => o.Save<SomethingElse>(Arg<SomethingElse>.Is.Anything));
    It Should_save_another_one = () => repo.AssertWasCalled(o => o.Save<AnotherOne>(Arg<AnotherOne>.Is.Anything));
}

结果是两个异常和一个通过。

第一次调用抛出:

System.InvalidOperationException: 没有设置要验证的期望,确保操作中的方法调用是虚拟 (C#) / 可覆盖 (VB.Net) 方法调用

第二个抛出:

System.InvalidOperationException:录制时仅在模拟方法调用中使用 Arg。预期 1 个参数,已定义 2 个。

第三个通过了……出于某种奇怪的原因。

我还尝试在我的设置中将 GenerateMock() 与 Expect 结合使用,并将 GenerateStub() 与 Stub 结合使用。两者都得到了完全相同的结果。我一定是做错了什么。

我正在使用: MachineSpec 0.3.0.0 和 RhinoMocks 3.6.0.0

有什么想法吗?

-----已修复------

这是在 Lee 的帮助下的完整(工作版本)。我正在使用一个额外的(非 linq)层。我的实际问题是我的一项测试在离线真实代码中重新使用了错误的 lambda 变量。 它 Should_do_something = () => repo.AssertWasCalled(o=>repo.Save(data)); //坏的lambda

所以这里有一个正确的测试样本供参考。

using System;
using System.Linq;
using System.Collections.Generic;
using Machine.Specifications;
using Rhino.Mocks;

namespace OnlineTesting.Specifications
{
    public interface Repository
    {
        void Save<T>(T data);
        IQueryable<T> All<T>();
    }

    public interface Service
    {
        void SaveItem(Item data);
        void SaveAnotherItem(AnotherItem data);
        void SaveOtherItem(OtherItem data);
        List<Item> GetItems();
        List<AnotherItem> GetAnotherItems();
        List<OtherItem> GetOtherItems();
    }

    public class ConcreteService : Service
    {
        Repository repo;
        public ConcreteService(Repository repo)
        {
            this.repo = repo;
        }
        public void SaveItem(Item data)
        {
            repo.Save(data);
        }
        public void SaveAnotherItem(AnotherItem data)
        {
            repo.Save(data);
        }
        public void SaveOtherItem(OtherItem data)
        {
            repo.Save(data);
        }

        public List<Item> GetItems()
        {
            return repo.All<Item>().ToList();
        }
        public List<AnotherItem> GetAnotherItems()
        {
            return repo.All<AnotherItem>().ToList();
        }
        public List<OtherItem> GetOtherItems()
        {
            return repo.All<OtherItem>().ToList();
        }
    }

    public class Item
    {
        public int Id { get; set; }
    }
    public class OtherItem
    {
    }
    public class AnotherItem
    {
    }


    public class When_something_else_happens
    {
        Establish context = () =>
        {
            _repository = MockRepository.GenerateMock<Repository>();
            _service = new ConcreteService(_repository);
            _controller = new TestController(_service);

            _repository.Stub(o => o.Save<Item>(Arg<Item>.Is.Anything)).WhenCalled(
                new Action<MethodInvocation>((o) =>
                {
                    var data = o.Arguments.FirstOrDefault() as Item;
                    if (data != null && data.Id == 0)
                        data.Id++;
                }));
        };

        Because of = () => _controller.DoSomethingElse();

        It should_save_the_first_thing = () =>
             _repository.AssertWasCalled(repo => repo.Save(Arg<Item>.Is.Anything));

        It should_save_the_other_thing = () =>
             _repository.AssertWasCalled(repo => repo.Save(Arg<OtherItem>.Is.Anything));

        It should_save_the_last_thing = () =>
             _repository.AssertWasCalled(repo => repo.Save(Arg<AnotherItem>.Is.Anything));

        static Repository _repository;
        static TestController _controller;
        static Service _service;
    }

    public class TestController
    {
        readonly Service _service;

        public TestController(Service service)
        {
            _service = service;
        }

        public void DoSomethingElse()
        {
            _service.SaveItem(new Item());
            _service.SaveOtherItem(new OtherItem());
            _service.SaveAnotherItem(new AnotherItem());
        }
    }
}

【问题讨论】:

  • 另外...我无法在调用之前设置参数,因为控制器操作构造了一个新对象并将其传递给存储库。所以我使用 Arg.Is.Anything。我只是想确保为特定类型调用 repo.save。

标签: c# rhino-mocks invalidoperationexception mspec generic-method


【解决方案1】:

似乎每个人都在掩饰的一点是,您不需要存根来执行“调用了断言”。而且,按照你写的方式,Arg&lt;T&gt;.Is.Anything,它会忽略类型。您没有验证任何通用约束。您想使用Arg&lt;T&gt;.Is.TypeOf&lt;T&gt;。查看documentation了解更多详情。

-----------------------------------------------
| Arg<T>.Is   |                               |
===============================================
| Anything()  | No constraints                |
-----------------------------------------------
| TypeOf<T>() | Argument is of a certain type |
-----------------------------------------------

您的固定代码 sn-p 仍然太复杂。您没有使用在“调用时”中保存的 data 对象。而且你不需要它来做一个简单的“断言被调用”。

@leebrandt 的代码看起来非常正确和简单,但没有引入自动模拟容器。

【讨论】:

    【解决方案2】:

    试试这个。

    [Subject("Test")]
    public class When_something_happens_with_constraint
    {
        static IRepository repo;
        static TestController controller;
        static ActionResult result;
    
        Establish context = () =>
        {
            repo = MockRepository.GenerateMock<IRepository>();
            controller = new TestController(repo);
        };
    
        //post data to a controller
        Because of = () => result = controller.SaveAction(new SomethingModel() { Name = "test", Description = "test" });
    
        //controller constructs its own something using the data posted, then saves it. I want to make sure three calls were made.  
        It Should_save_something = () => repo.AssertWasCalled(o => o.Save(Arg<Something>.Is.Anything));
        It Should_save_something_else = () => repo.AssertWasCalled(o => o.Save(Arg<SomethingElse>.Is.Anything));
        It Should_save_another_one = () => repo.AssertWasCalled(o => o.Save(Arg<AnotherOne>.Is.Anything));
    }
    

    【讨论】:

    • 我什至可能会创建一个模块级别的 SomethingModel,以便您可以验证它实际上是发送到存储库进行保存的那个。
    • 谢谢李。在测试了您的并将其与我的进行比较后,我发现了问题。我在上面发布了完整的结果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-20
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-06
    相关资源
    最近更新 更多