Как я могу вызвать событие на издевательском BackgroundWorker?

Я пытаюсь проверить, как мой класс реагирует на то, что происходит, когда BackgroundWorker запускает событие RunWorkerCompleted.

Я использую RhinoMocks (если есть другой подход, я тоже хочу его попробовать), и код выглядит следующим образом:

//arrange
var bw1 = MockRepository.GenerateStub<BackgroundWorker>();
Action work1 = () => Thread.Sleep(1);
WorkQueueProcess processInQueue = new WorkQueueProcess(bw1) { Work = work1 };
var tested = new WorkQueue() { processInQueue };

// act
bw1.Raise(
    bw => bw.RunWorkerCompleted += 
        null, 
        bw1, 
        new RunWorkerCompletedEventArgs(null, null, false)
);

// assert
Assert.AreEqual(false, tested.IsBusy);

Я получаю исключение, которое говорит:

Недопустимый вызов, последний вызов был использован или вызов не был выполнен (убедитесь, что вы вызываете виртуальный (C#)/переопределяемый (VB) метод).

Что я делаю неправильно ? Это потому, что у BackgroundWorker нет виртуальных методов? Я думал, что должен иметь возможность поднять событие независимо, потому что событие почти никогда не бывает виртуальным.


person Tomas Pajonk    schedule 22.09.2009    source источник


Ответы (1)


Только класс, который определяет событие, может вызвать это событие. Просто так работает система событий в .NET.

Метод Raise, предоставляемый RhinoMocks, предназначен для использования с интерфейсами, определяющими события. В этом случае любой класс, реализующий этот интерфейс, владеет собственным событием и, следовательно, может его вызвать. Это также верно для типов, генерируемых во время выполнения, сгенерированных RhinoMocks.

Однако, когда дело доходит до классов, даже подтипы не могут вызывать события, определенные их супертипами, поэтому у нас есть идиома кодирования OnEvent.

BackgroundWorker имеет метод OnRunWorkerCompleted, который вызовет событие. Этот метод является виртуальным, поэтому, если вы можете заставить RhinoMocks вызывать этот защищенный метод, вы сможете вызвать событие.

Я использую Moq, который не может этого сделать — я не могу вспомнить, есть ли у RhinoMocks возможность вызывать защищенные элементы.

В противном случае вы можете получить класс, специфичный для теста, из BackgroundWorker и добавить общедоступный метод, который вызывает OnRunWorkerCompleted.

Однако в качестве заключительного замечания я бы настоятельно рекомендовал вам не пытаться выполнять модульное тестирование многопоточного кода — на этом пути вас ждет только боль...

person Mark Seemann    schedule 22.09.2009
comment
Большое спасибо, что напомнили мне об идиоме (идиоме OnEvent). Я не думал об этом, хотя сталкивался с этим раньше. RhinoMocks, похоже, не может вызывать защищенных членов, но создание моего собственного макета фонового рабочего, похоже, справится с этой задачей. Я пытаюсь проверить, будет ли мой объект правильно реагировать на результаты фонового рабочего процесса, я даже не собираюсь пробовать многопоточное модульное тестирование. Однако спасибо за предупреждение. - person Tomas Pajonk; 23.09.2009