Rhino Mocks Синтаксис AAA и безопасность потоков - PullRequest
1 голос
/ 19 ноября 2010

У меня есть проблема, когда мои тесты проходят большую часть времени, но иногда дают сбой.Я думаю, что это может быть проблема с многопоточностью, но я экспериментировал с блокировками, сном и т. Д., Чтобы попытаться найти причину проблемы без удачи.Я использую правильный синтаксис?Через некоторое время я возвращаюсь к использованию Rhino.

Вот обеззараженная версия моих тестов.Как я уже сказал, они работают 3 из 4 раз, но затем бум.

[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.Sleep - это, как правило, запах кода, и я бы хотел его заблокировать, но я стараюсь быть прагматичным неаккуратным .Пожалуйста, дайте мне знать, если у вас есть лучший способ.

1 Ответ

5 голосов
/ 19 ноября 2010

Проблема в том, что вы можете получить в конце методов тестирования, где вы разместили утверждения, в то время как дочерний поток мог выполнить свою работу или нет. С вашей точки зрения, это почти случайно.

Что вы можете сделать, и это то, что я делаю при тестировании потоковых методов, - это использовать объект синхронизации, такой как ManualResetEvent. Вы сбрасываете его на false в начале методов тестирования:

var wait = new ManualResetEvent(false);

Затем, когда ваш макет вызывается тестируемым объектом, вы устанавливаете его статус:

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

Теперь, перед вашим кодом блока утверждений, вы пишете что-то вроде

Assert(wait.WaitOne(timeoutOfYourChoice));

Это оставит вторичному потоку достаточно времени, чтобы закончить свою работу.

...