我正在尝试学习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个已被定义。
第三个通过...出于某种奇怪的原因。
我也尝试在我的设置中使用Expect的GenerateMock()以及使用Stub的GenerateStub()。两者都得到了完全相同的结果。我一定做错了。
我正在使用: MachineSpec 0.3.0.0和RhinoMocks 3.6.0.0
有什么想法吗?
----- ---------- FIXED
这是李的帮助下的完整(工作版)。我正在使用额外的(非linq)图层。我的实际问题是我的一个测试在离线实际代码中重用了错误的lambda变量。 它Should_do_something =()=&gt; repo.AssertWasCalled(的 0 =&GT;的回购 .Save(数据)); //坏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());
}
}
}
答案 0 :(得分:1)
每个人似乎都在掩饰的一点是,不需要存根才能执行“断言被调用”。而且,你编写的方式Arg<T>.Is.Anything
,它将忽略类型。您没有验证任何通用约束。您想使用Arg<T>.Is.TypeOf<T>
。查看documentation了解详情。
-----------------------------------------------
| Arg<T>.Is | |
===============================================
| Anything() | No constraints |
-----------------------------------------------
| TypeOf<T>() | Argument is of a certain type |
-----------------------------------------------
您的已修复代码段仍然过于复杂。您没有使用“在调用时”保存的data
对象。并且你不需要它来做一个简单的“断言被称为”。
@ leebrandt的代码看起来正确而简单,没有引入自动插件容器。
答案 1 :(得分:0)
试一试。
[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));
}