Как запустить параллельный юнит-тест? - PullRequest
44 голосов
/ 04 августа 2009

Как использовать junit для запуска теста параллелизма?

Допустим, у меня есть класс

public class MessageBoard
{
    public synchronized void postMessage(String message)
    {
        ....
    }

    public void updateMessage(Long id, String message)
    {
        ....
    }
}

Я хочу одновременно протестировать множественный доступ к этому postMessage.Любой совет по этому поводу?Я хочу запустить этот вид теста параллелизма для всех моих функций сеттера (или любого метода, который включает в себя операцию создания / обновления / удаления).

Ответы [ 12 ]

21 голосов
/ 04 августа 2009

К сожалению, я не верю, что вы можете окончательно доказать, что ваш код является поточно-ориентированным, с помощью тестирования во время выполнения. Вы можете создать столько потоков, сколько захотите, и это может / не может пройти в зависимости от расписания.

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

16 голосов
/ 07 августа 2009

Я бы рекомендовал использовать MultithreadedTC - Автор самого мастера параллелизма Билл Пью Nat Ayewah ). Цитата из их обзора :

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

Эта структура позволяет детерминистически проверять каждое чередование потоков в отдельных тестах

9 голосов
/ 04 августа 2009

Вы можете только доказать наличие одновременных ошибок, но не их отсутствие .

Однако вы можете написать специализированный тестовый прогон, который порождает несколько параллельных потоков и затем вызывает аннотированные методы @Test.

7 голосов
/ 05 августа 2009

В .NET есть такие инструменты, как TypeMock Racer или Microsoft CHESS , которые предназначены специально для параллельного тестирования модулей. Эти инструменты не только находят ошибки многопоточности, такие как взаимоблокировки, но также предоставляют набор чередований потоков, которые воспроизводят ошибки.

Я думаю, что есть что-то похожее в мире Java.

5 голосов
/ 29 октября 2011

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

Работа Mycila над JUnit ConcurrentJunitRunner и ConcurrentSuite очень интересна. Статья кажется немного устаревшей по сравнению с последней версией GA, в моих примерах я покажу обновленное использование.

Аннотирование тестового класса, подобного следующему, приведет к одновременному выполнению тестовых методов с уровнем параллелизма 6:

import com.mycila.junit.concurrent.ConcurrentJunitRunner;
import com.mycila.junit.concurrent.Concurrency;

@RunWith(ConcurrentJunitRunner.class)
@Concurrency(6)
public final class ATest {
...

Вы также можете запустить все тестовые классы одновременно:

import com.mycila.junit.concurrent.ConcurrentSuiteRunner;

@RunWith(ConcurrentSuiteRunner.class)
@Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class})
public class MySuite {
}

Зависимость Maven :

<dependency>
    <groupId>com.mycila</groupId>
    <artifactId>mycila-junit</artifactId>
    <version>1.4.ga</version>
</dependency> 

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

@Test
public final void runConcurrentMethod() throws InterruptedException {
    ExecutorService exec = Executors.newFixedThreadPool(16);
    for (int i = 0; i < 10000; i++) {
        exec.execute(new Runnable() {
             @Override
             public void run() {
                 concurrentMethod();
             }
        });
    }
    exec.shutdown();
    exec.awaitTermination(50, TimeUnit.SECONDS);
}

private void concurrentMethod() {
    //do and assert something
}

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

2 голосов
/ 22 марта 2010

Попробуйте посмотреть на ActiveTestSuite, который поставляется с JUnit. Он может одновременно запускать несколько тестов JUnit:

public static Test suite()
{
    TestSuite suite = new ActiveTestSuite();
    suite.addTestSuite(PostMessageTest.class);
    suite.addTestSuite(PostMessageTest.class);
    suite.addTestSuite(PostMessageTest.class);
    suite.addTestSuite(PostMessageTest.class);
    suite.addTestSuite(PostMessageTest.class);
    return suite;
}

Вышеуказанный класс теста JUnit будет запускаться 5 раз в параллельном режиме. Если вы хотели вариации в ваших тестах paralell, просто создайте другой класс.

1 голос
/ 22 марта 2010

Вы можете использовать библиотеку tempus-fugit для параллельного и многократного запуска методов тестирования, чтобы имитировать среду типа нагрузочного тестирования. Хотя предыдущий комментарий указывает на то, что метод post синхронизирован и так защищен, могут быть задействованы связанные члены или методы, которые сами не защищены, поэтому возможно , что тест типа нагрузки / выдержки может поймать это. Я бы посоветовал вам установить довольно крупнозернистый / сквозной тест, чтобы дать вам лучший шанс поймать любые петлевые отверстия.

См. Раздел интеграции JUnit документации .

Кстати, я разработчик указанного проекта:)

1 голос
/ 06 августа 2009

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

1 голос
/ 05 августа 2009

В вашем примере метод postMessage () имеет значение synchronized , поэтому вы фактически не увидите каких-либо эффектов параллелизма внутри одной виртуальной машины, но вы сможете оценить производительность синхронизированной версии.

Вам нужно будет запустить несколько копий тестовой программы одновременно на разных виртуальных машинах. Вы можете использовать

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

Process running[] = new Process[5];
for (int i = 0; i < 5; i++) {
 ProcessBuilder b = new ProcessBuilder("java -cp " + getCP() + " MyTestRunner");
 running[i] = b.start();
}

for(int i = 0; i < 5; i++) {
 running[i].waitFor();
}

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

public void testMesageBoard() {
 final MessageBoard b = new MessageBoard();

 int n = 5;
 Thread T[] = new Thread[n];
 for (int i = 0; i < n; i++) {
  T[i] = new Thread(new Runnable() {
   public void run() {
    for (int j = 0; j < maxIterations; j++) {
      Thread.sleep( random.nextInt(50) );
      b.postMessage(generateMessage(j));
      verifyContent(j); // put some assertions here
    }
   }
  });

  PerfTimer.start();
  for (Thread t : T) {
   t.start();
  }

  for (Thread t : T) {
   t.join();
  }
  PerfTimer.stop();
  log("took: " + PerfTimer.elapsed());
 }
}**strong text**
0 голосов
/ 08 декабря 2013

Вы также можете попробовать HavaRunner . По умолчанию он запускает тесты параллельно.

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