В настоящее время мы сталкиваемся с некоторыми проблемами во время модульного тестирования. Наш класс выполняет многопоточную обработку некоторых вызовов функций для Mocked-объектов с помощью Rhino Mocks. Вот пример, сокращенный до минимума:
public class Bar
{
private readonly List<IFoo> _fooList;
public Bar(List<IFoo> fooList)
{
_fooList = fooList;
}
public void Start()
{
var allTasks = new List<Task>();
foreach (var foo in _fooList)
allTasks.Add(Task.Factory.StartNew(() => foo.DoSomething()));
Task.WaitAll(allTasks.ToArray());
}
}
Интерфейс IFoo определяется как:
public interface IFoo
{
void DoSomething();
event EventHandler myEvent;
}
Для воспроизведения тупиковой ситуации наш unittest делает следующее: 1. создайте несколько IFoo Mocks 2. Вызов myEvent при вызове DoSomething ().
[TestMethod]
public void Foo_RaiseBar()
{
var fooList = GenerateFooList(50);
var target = new Bar(fooList);
target.Start();
}
private List<IFoo> GenerateFooList(int max)
{
var mocks = new MockRepository();
var fooList = new List<IFoo>();
for (int i = 0; i < max; i++)
fooList.Add(GenerateFoo(mocks));
mocks.ReplayAll();
return fooList;
}
private IFoo GenerateFoo(MockRepository mocks)
{
var foo = mocks.StrictMock<IFoo>();
foo.myEvent += null;
var eventRaiser = LastCall.On(foo).IgnoreArguments().GetEventRaiser();
foo.DoSomething();
LastCall.On(foo).WhenCalled(i => eventRaiser.Raise(foo, EventArgs.Empty));
return foo;
}
Чем больше генерируется Foo, тем чаще возникает взаимоблокировка. Если тест не блокирует, запустите его несколько раз, и он будет. Остановка тестового запуска отладки показывает, что все задачи по-прежнему находятся в TaskStatus.Running, а текущий рабочий поток прерывается на
[В спящем режиме, подождите или присоединяйтесь]
Rhino.Mocks.DLL! Rhino.Mocks.Impl.RhinoInterceptor.Intercept (Castle.Core.Interceptor.IInvocation invocation) + 0x3d bytes
Самая странная вещь, которая нас больше всего сбивает с толку, это то, что сигнатура метода Intercept (...) определена как синхронизированная, но здесь расположены несколько потоков. Я прочитал несколько сообщений о Rhino Mocks и Multithreaded, но не обнаружил предупреждений (ожидаемая установка записей) или ограничений.
[MethodImpl(MethodImplOptions.Synchronized)]
public void Intercept(IInvocation invocation)
Мы делаем что-то совершенно неправильно при настройке наших Mockobjects или их использовании в многопоточной среде? Любая помощь или подсказка приветствуются!