Как исправить многопоточный модульный тест, который зависает в нашей сборке TeamCity?

#c# #teamcity #rhino-mocks

#c# #teamcity #rhino-издевается

Вопрос:

Мы столкнулись с проблемой, заключающейся в том, что один из наших модульных тестов несколько раз за день завершался неудачей во время CI на TeamCity. Мы используем MSTest и RhinoMocks. Это почти невозможно воспроизвести на компьютере разработчика (возможно, из-за 1 процессора на компьютере разработчика, я не знаю).

 [TestMethod]
[TestCategory("UnitTest")]
[ExpectedException(typeof(AggregateException))]
public void TestRiskDataStagingThrowAggregateException()
{
    DataStagingThrowException(typeof(DeskRiskDataPump));
}

protected void DataStagingThrowException(Type dataPumpType)
{
    // set expectations
    foreach (IUploader uploader in StubUploaders)
    {
        uploader.Expect(x => x.Upload(StubDataProvider)).Repeat.Once().Throw(new Exception("BANG!!!"));
    }

    Repository.ReplayAll();

    IDataPump datapump = new DataPumpFactory().GetDataPump(dataPumpType, StubDataProvider, StubUploaders, StubDistributor);

    // Execute
    // the test can hang here!!!
    datapump.Execute();

    Repository.VerifyAll();
}

StubDataProvider = Repository.Stub<IEnumerable<TRecord>>();
StubUploaders = new List<IUploader>();
StubDistributor = Repository.Stub<IDataDistributor>();

StubUploaders.Add(Repository.Stub<IUploader>());
StubUploaders.Add(Repository.Stub<IUploader>());
StubUploaders.Add(Repository.Stub<IUploader>());
  

Класс, который реализует IDataPump , немного усложнен, и я боюсь публиковать его здесь 🙂 В общем, он реализует шаблон Производитель-потребитель и запускает несколько процессов в разных потоках, что-то вроде этого

 Task[] tasks = new Task[4];

// Start adding to the queue
tasks[0] = Task.Factory.StartNew(Produce);

// Start draining the queue in parallel
tasks[1] = Task.Factory.StartNew(ConsumeCreditRisk);
tasks[2] = Task.Factory.StartNew(ConsumeTrancheRisk);
tasks[3] = Task.Factory.StartNew(ConsumeRatesRisk);

// Wait to complete
try
{
    Task.WaitAll(tasks);
}
catch (AggregateException e)
{
    .....
    throw;
}

private void ConsumeCreditRisk()
{
    _creditRiskUploader.Upload(_creditRiskBuffer.GetConsumingEnumerable());
}
  

В нашем случае загрузчики — это заглушки, которые предполагают выдачу исключения, и оно должно быть перехвачено как совокупное исключение. Но, согласно журналам, он иногда зависает при загрузке метода stub.

У вас есть какие-либо предположения, что может вызвать проблему?

Комментарии:

1. Это почти наверняка какая-то проблема параллелизма. У вас есть свой собственный код блокировки или вы используете коллекции из System. Коллекции. Одновременный доступ задач к синхронизации к данным? Я бы рекомендовал добавить модульный тестовый цикл, который выполняет ваш тест локально навсегда.. это должно позволить вам (в конечном итоге) испытывать такое же зависание, даже когда отладчик подключен.

2. спасибо за совет, я попробую. Для организации буфера мы используем BlockingCollection. Этот код прошел сложный нагрузочный тест … он должен зависнуть в таком простом модульном тесте

Ответ №1:

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