Подтвердить что ты не робот

Как запустить concurrency unit test?

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

Скажем, у меня есть класс

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Я бы порекомендовал использовать MultithreadedTC - написан сам мастером concurrency Bill PughNat Ayewah). Цитата из их overview:

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

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

Ответ 3

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

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

Ответ 4

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

Я бы предположил, что в мире Java есть нечто похожее.

Ответ 5

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

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

Аннотирование тестового класса, подобного приведенному ниже, приведет к одновременному выполнению тестовых методов с уровнем concurrency 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
}

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

Ответ 6

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

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

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

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();
}

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

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**

Ответ 7

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

Ответ 8

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

См. раздел интеграции JUnit documentation.

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

Ответ 9

Попробуйте взглянуть на 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, просто создайте другой класс.

Ответ 10

TestNG поддерживает тестирование concurrency в Java. Этот article описывает, как его можно использовать, и есть документы на сайте testng.

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

Ответ 11

Лучший подход здесь - использовать системные тесты, чтобы взорвать ваш код с запросами, чтобы увидеть, не падает ли он. Затем используйте unit test для проверки логической корректности. Способ, которым я хотел бы это сделать, - создать прокси-сервер для асинхронного вызова и выполнить его синхронно при тестировании.

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

Ответ 12

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

Ответ 13

Вы можете проверить IMUnit. Он совместим с JUnit.