我对这里发生的事情感到有些困惑。我正在查看Atomic Object中的Puzzle示例,展示如何测试Model-View-Presenter模式Puzzle.zip
View有一个私人活动。该视图还有一个Subscribe(委托)函数,用于将委托添加到事件中。 Presenter在IView和IModel中传递。在构造期间,它订阅视图并将其连接到模型上的函数。
对于Presenter的单元测试,使用NMock模拟View类。所以它只是一个愚蠢的类,而且Subscribe()函数实际上并没有做任何事情。当然,要测试演示者,您必须模拟视图和模型,然后在视图中触发事件并确保调用模型函数。示例代码工作正常 - 但是,我不明白它是如何工作的!
一些摘录:
private DynamicMock modelMock;
private IPuzzleModel model;
private DynamicMock viewMock;
private IPuzzleView view;
private SavedTypeOf moveRequestConstraint;
[SetUp]
public void SetUp()
{
modelMock = new DynamicMock(typeof(IPuzzleModel));
modelMock.Strict = true;
model = modelMock.MockInstance as IPuzzleModel;
// Setup the view
viewMock = new DynamicMock(typeof(IPuzzleView));
viewMock.Strict = true;
view = viewMock.MockInstance as IPuzzleView;
moveRequestConstraint = new SavedTypeOf(typeof(PointDelegate));
viewMock.Expect("SubscribeMoveRequest", moveRequestConstraint);
// create the presenter
new PuzzlePresenter(model, view);
}
[Test]
public void test_MoveRequest_fromView()
{
Point point = new Point(1, 2);
modelMock.Expect("MoveRequest", point);
PointDelegate trigger = moveRequestConstraint.GetInstance as PointDelegate;
trigger(point);
}
不知何故,“触发(点)”调用实际上是连接到视图,并导致视图中的私有事件触发。我无法弄清楚它是如何工作的 - 我没有看到它连接到视图实例的位置。我错过了什么?
更新:我正在尝试使用NMock 2.看起来moveRequestConstraint变量接收传递给TestSetup函数中的SubscribeMoveRequest()的值。但是,这是NMock 1语法 - 而NMock 2似乎不支持该语法。我如何使用NMock 2进行操作?
答案 0 :(得分:1)
您是否在测试中执行任何域代码?你可能没有测试任何东西,除了演示者的构造函数没有抛出异常。
顺便说一句,我强烈建议您使用RhinoMocks。它看起来像这样:
private IPuzzleModel model;
private IPuzzleView view;
private PointDelegate pointDelegate;
private Point point;
[SetUp]
public void SetUp()
{
model = MockRepository.CreateMock<IPuzzleModel>();
view = MockRepository.CreateMock<IPuzzleView>();
// get the delegate passed to the mock when it is called
// This is one of the more complex things you do with mocks.
view.Stub(x => x.Subscribe(Arg<PontDelegate>().Is.Anything)
.WhenCalled(call => pointDelegate = (PointDelegate)call.Arguments[0];);
point = new Point(1, 2);
}
[Test]
public void test_MoveRequest_fromView()
{
PuzzlePresenter presenter = new PuzzlePresenter(model, view);
// make sure the Delegate method was called and the delegate
// is available
Assert.IsNotNull(pointDelegate);
// fire the delegate.
pointDelegate(point);
// check if the model was called.
model.AssertWasCalled(x => x.MoveRequest(point));
}
答案 1 :(得分:0)
我遇到了同样的问题,试图使Presenter First示例与NMock2一起工作。
经过一番挖掘后,我在SourceForge上的NMock2论坛中找到了post。
[Test]
public void test_MoveRequest_fromView()
{
Mockery mockery = new Mockery();
IPuzzleView view = mockery.NewMock<IPuzzleView>();
IPuzzleModel model = mockery.NewMock<IPuzzleModel>();
CollectAction collect = new CollectAction(0);
Expect.Once.On(view).Method("SubscribeMoveRequest").Will(collect);
Expect.Once.On(model).Method("MoveRequest");
new PuzzlePresenter(model, view);
Point point = new Point(1, 2);
PointDelegate del = collect.Parameter as PointDelegate;
del(point);
mockery.VerifyAllExpectationsHaveBeenMet();
}
尝试上面的代码 - 没试过但它应该有用。它不像其他的NMock2读得那么好,但是NMock中的原始测试代码也没有。
<强>更新强>
似乎最新的NMock2(2.0.3411.37113)也支持通用版CollectAction,所以你也可以这样做:
PointDelegate savedPointDelegate = null;
CollectAction<PointDelegate> collect = new CollectAction<PointDelegate>(0,
delegate(PointDelegate del) { savedPointDelegate = del; });
...
savedPointDelegate(point);
以下是我对可读性改进的尝试,但这并没有太大的改进:
Expect.Once.On(view).Method("SubscribeMoveRequest").Will(
Collect.Argument<PointDelegate>(0, delegate(PointDelegate del) { savedPointDelegate = del; }));
public class Collect
{
public static CollectAction<T> Argument<T>(int index, CollectAction<T>.Collect collectDelegate)
{
CollectAction<T> collect = new CollectAction<T>(index, collectDelegate);
return collect;
}
}