Rhino Mocks AAA 语法和线程安全

发布于 2024-10-03 12:15:20 字数 3046 浏览 2 评论 0原文

我遇到一个问题,我的测试大多数都通过,但偶尔会失败。我认为这可能是一个线程问题,但我已经尝试过锁定和睡眠等,试图找出问题所在,但没有运气。我使用的语法正确吗?离开一段时间后,我又重新开始使用 Rhino。

这是我的测试的清理版本。正如我所说,他们四分之三会工作,但随后就会繁荣。

[TestFixture]
public class Tests
{
    [Test]
    public void ReprocessItems()
    {
        // Arrange
        Presenter presenter = new Presenter();

        Model model = new Model();

        model.Data.Add(new ViewModel(new Item { Id = 1 }) { IsSelected = true });
        model.Data.Add(new ViewModel(new Item { Id = 2 }) { IsSelected = true });
        model.Data.Add(new ViewModel(new Item { Id = 3 }) { IsSelected = false });
        model.Data.Add(new ViewModel(new Item { Id = 4 }) { IsSelected = false });

        presenter.Model = model;

        var gateway = MockRepository.GenerateStub<IGateway>();
        presenter.Gateway = gateway;

        // Act
        presenter.ReprocessItems();

        // Assert
        gateway.AssertWasCalled(o => o.ReprocessItem(1, presenter.ReprocessDone));
        gateway.AssertWasCalled(o => o.ReprocessItem(2, presenter.ReprocessDone));
    }

    [Test]
    public void ShowItemReferralCommentary()
    {
        // Arrange
        Presenter presenter = new Presenter();

        Model model = new Model();
        model.Data.Add(new ViewModel(new Item { Id = 1 }) { IsSelected = true });
        model.Data.Add(new ViewModel(new Item { Id = 2 }) { IsSelected = false });

        presenter.Model = model;

        var gateway = MockRepository.GenerateStub<IGateway>();
        presenter.Gateway = gateway;

        var view = MockRepository.GenerateStub<IView>();
        presenter.View = view;

        gateway.Stub(x => x.RequestItemCommentary(1)).Return("This is some commentary");

        // Act
        presenter.ShowItemReferralCommentary();

        // Assert
        gateway.AssertWasCalled(o => o.RequestItemCommentary(1));

        view.AssertWasCalled(o => o.ShowMessageBox("This is some commentary", "Referral Commentary"));
    }

    [Test]
    public void AcceptSelectedItems()
    {
        // Arrange
        Presenter presenter = new Presenter();

        Model model = new Model();
        model.Data.Add(new ViewModel(new Item { Id = 1 }) { IsSelected = false });
        model.Data.Add(new ViewModel(new Item { Id = 2 }) { IsSelected = false });
        model.Data.Add(new ViewModel(new Item { Id = 3 }) { IsSelected = true });
        model.Data.Add(new ViewModel(new Item { Id = 4 }) { IsSelected = true });

        presenter.Model = model;

        var gateway = MockRepository.GenerateStub<IGateway>();
        presenter.Gateway = gateway;

        // Act
        presenter.AcceptSelectedItems();

        // Assert
        gateway.AssertWasCalled(o => o.AcceptItem(3, presenter.AcceptCompleted));
        gateway.AssertWasCalled(o => o.AcceptItem(4, presenter.AcceptCompleted));
    }
}

编辑: ** 根据下面的评论,我通过添加 Thread.Sleep(500); 暂时解决了这个问题。在每个断言之前。 Thread.Sleeps 通常是一种代码味道,我想将其锁定,但我试图变得马虎务实。如果您有更好的方法,请告诉我。

I have an issue where my tests pass most of the time but occasionally fail. I think it might be a threading issue, but I've experimented with locks and sleeps etc to try and find where the problems lies with no luck. Am I using the correct syntax? I'm getting back into using Rhino after a while away.

Here's a sanitised version of my tests. As I say, they work 3 out of 4 times, but then boom.

[TestFixture]
public class Tests
{
    [Test]
    public void ReprocessItems()
    {
        // Arrange
        Presenter presenter = new Presenter();

        Model model = new Model();

        model.Data.Add(new ViewModel(new Item { Id = 1 }) { IsSelected = true });
        model.Data.Add(new ViewModel(new Item { Id = 2 }) { IsSelected = true });
        model.Data.Add(new ViewModel(new Item { Id = 3 }) { IsSelected = false });
        model.Data.Add(new ViewModel(new Item { Id = 4 }) { IsSelected = false });

        presenter.Model = model;

        var gateway = MockRepository.GenerateStub<IGateway>();
        presenter.Gateway = gateway;

        // Act
        presenter.ReprocessItems();

        // Assert
        gateway.AssertWasCalled(o => o.ReprocessItem(1, presenter.ReprocessDone));
        gateway.AssertWasCalled(o => o.ReprocessItem(2, presenter.ReprocessDone));
    }

    [Test]
    public void ShowItemReferralCommentary()
    {
        // Arrange
        Presenter presenter = new Presenter();

        Model model = new Model();
        model.Data.Add(new ViewModel(new Item { Id = 1 }) { IsSelected = true });
        model.Data.Add(new ViewModel(new Item { Id = 2 }) { IsSelected = false });

        presenter.Model = model;

        var gateway = MockRepository.GenerateStub<IGateway>();
        presenter.Gateway = gateway;

        var view = MockRepository.GenerateStub<IView>();
        presenter.View = view;

        gateway.Stub(x => x.RequestItemCommentary(1)).Return("This is some commentary");

        // Act
        presenter.ShowItemReferralCommentary();

        // Assert
        gateway.AssertWasCalled(o => o.RequestItemCommentary(1));

        view.AssertWasCalled(o => o.ShowMessageBox("This is some commentary", "Referral Commentary"));
    }

    [Test]
    public void AcceptSelectedItems()
    {
        // Arrange
        Presenter presenter = new Presenter();

        Model model = new Model();
        model.Data.Add(new ViewModel(new Item { Id = 1 }) { IsSelected = false });
        model.Data.Add(new ViewModel(new Item { Id = 2 }) { IsSelected = false });
        model.Data.Add(new ViewModel(new Item { Id = 3 }) { IsSelected = true });
        model.Data.Add(new ViewModel(new Item { Id = 4 }) { IsSelected = true });

        presenter.Model = model;

        var gateway = MockRepository.GenerateStub<IGateway>();
        presenter.Gateway = gateway;

        // Act
        presenter.AcceptSelectedItems();

        // Assert
        gateway.AssertWasCalled(o => o.AcceptItem(3, presenter.AcceptCompleted));
        gateway.AssertWasCalled(o => o.AcceptItem(4, presenter.AcceptCompleted));
    }
}

EDIT: ** As per a comment below, I have temporarily solved this by adding a Thread.Sleep(500); before each assert. Thread.Sleeps are generally a code smell, and I'd like to lock it down but I'm trying to be sloppy pragmatic. Please do let me know if you have a better way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

如歌彻婉言 2024-10-10 12:15:20

问题是,您可能会到达测试方法的末尾(放置断言的位置),而子线程可能已经完成了其工作,也可能没有完成。从你的角度来看,这几乎是随机的。

您可以做的,也是我在测试线程方法时所做的,是使用同步对象,例如 ManualResetEvent。您在测试方法开始时将其重置为 false:

var wait = new ManualResetEvent(false);

然后,当正在测试的对象调用您的模拟时,您设置其状态:

Expect.Call(() => yourMock.yourMethod(whatever)).WhenCalled(x => wait.Set() );

现在,在断言块代码之前,您编写类似的内容

Assert(wait.WaitOne(timeoutOfYourChoice));

这将为辅助线程留下足够的时间来完成他们的工作。

The problem is that you could get at the end of the test methods, where you placed the assertions, while the children thread could have done their job or not. To your point of view, it's almost random.

What you could do, and that's what I do when I test threaded methods, is to use a synchronization object such as ManualResetEvent. You reset it to false at the test methods beginning:

var wait = new ManualResetEvent(false);

Then, when your mock is invoked by the object being tested, you set its status:

Expect.Call(() => yourMock.yourMethod(whatever)).WhenCalled(x => wait.Set() );

Now, before your assertion block code, you write something like

Assert(wait.WaitOne(timeoutOfYourChoice));

This will leave secondary thread enough time to finish their work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文