2013-08-14 5 views
5

Как практикующий TDD, я хочу проверить все, что я кодирую.Библиотека для утверждения многопоточного кода на Java

В течение последних нескольких лет я кодировал многопотоковый код, и одна часть моих тестов очень беспокоила.

Когда я должен утверждать то, что может произойти во время цикла запуска() я в конечном итоге с какой-то вид O утверждение, как это:

assertEventually(timeout, assertion) 

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

Так что мой вопрос: кто-нибудь знает библиотеку, которая предоставляет это?

Вот мое решение до сих пор:

private void assertEventually(int timeoutInMilliseconds, Runnable assertion){ 
    long begin = System.currentTimeMillis(); 
    long now = begin; 
    Throwable lastException = null; 
    do{ 
     try{ 
      assertion.run(); 
      return; 
     }catch(RuntimeException e){ 
      lastException = e; 
     }catch(AssertionError e){ 
      lastException = e; 
     } 
     now = System.currentTimeMillis(); 
    }while((now - begin) < timeoutInMilliseconds); 
    throw new RuntimeException(lastException); 
} 

Используя это заканчивается так:

assertEvetuallyTrue(1000, new Runnable() { 
     public void run() { 
      assertThat(Thread.activeCount()).isEqualTo(before); 
     } 
    }); 
+0

Что вы утверждающего в образце кода? имя метода подразумевает утверждение, что что-то имеет значение true, но код выполняет только Runnable и гарантирует, что он не выйдет из строя. – dkatzel

+0

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

+0

Я бы ожидал, что assertEventually выйдет за исключение, которое он истечет. В вашем примере похоже, что он пытается сбросить нуль. Кроме того, имеет смысл бросить «AssertError». – Gray

ответ

2

Две библиотеки, которые вы можете захотеть, чтобы проверить:

  • ConcurrentUnit - позволяет выполнять утверждения из любого потока, подождав определенное количество времени
  • Awaitility - позволяет ждать определенное количество времени или до тех пор, пока не будет выполнено какое-либо условие.
+0

Спасибо, ConcurrentUnit делает трюк =] –

1

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

Глядя на этом конкретном примере, учитывая использование многопоточности я хотел бы предложить что-то вроде этого:

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

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

+0

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

+0

Моя позиция заключается в том, что принятие такого подхода к тестированию делает его (а) более трудным для тестирования и (б) приводит к более хрупким испытаниям. Если вы проводите тесты с более высокого уровня (т. Е. API, открытый вашим приложением), вы можете структурировать свои тесты таким образом, чтобы не справляться с этими проблемами.Тем не менее, я хотел бы, чтобы кто-то доказал, что я ошибаюсь, поскольку это потенциально может быть полезной техникой, если кто-то может показать хороший подход, который дает очень четкое и надежное утверждение о прохождении/сбое. – robjohncox

+0

Я согласен с тобой. Этот тип теста не должен использоваться для модульного тестирования ваших классов по тем же причинам, о которых вы говорили. Тем не менее, наступает время, когда все это должно быть собрано вместе, и вам нужно проверить их. И вот когда такое утверждение пригодится. –

0

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

Единичные тесты будут касаться только одного образца, и они должны быть детерминированными. Вместо этой конструкции check-and-loop я думаю, что вы должны сообщить об этом потоку JUnit через какой-то механизм, как только произойдет событие, на которое вы хотите утвердить. Обратные вызовы, фьючерсы, затворы, обменники и т. Д. Могут использоваться для создания таких механизмов.

Это сложная задача для создания вашего многопоточного кода, чтобы его можно было протестировать без проверки и контура или ожидание состояния в состоянии.

Но если вы действительно должны это сделать, используйте System.nanoTime() вместо System.currentTimeMillis(), потому что он обновляется чаще, хотя фактическая реализация и точность обоих часов зависят от вашей версии JVM, операционной системы и оборудования.

1

Если вы пришли из Scala, вы можете использовать его в качестве знака eventually.

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

Там есть обертка Java сделанные halfvim здесь: https://github.com/halfvim/eventually

Смежные вопросы