Несколько выражений RunWith в jUnit

113

Я пишу модульный тест и хочу использовать JUnitParamsRunnerи MockitoJUnitRunnerдля одного тестового класса.

К сожалению, не работает следующее:

@RunWith(MockitoJUnitRunner.class)
@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {
  // some tests
}

Есть ли способ использовать и Mockito, и JUnitParams в одном тестовом классе?

Ханс-Хельге
источник
2
Посмотрите: code.google.com/p/junitparams/wiki/MergingWithOtherRunners
Алексей Гаврилов,
2
Здесь также есть хороший пример: blog.project13.pl/index.php/coding/1077/…
falsarella

Ответы:

110

Вы не можете этого сделать, потому что в соответствии со спецификацией вы не можете дважды поместить одну и ту же аннотацию на один и тот же аннотированный элемент.

Итак, какое решение? Решение состоит в том, чтобы поставить только один @RunWith()бегунок, без которого вы не можете стоять, и заменить другой на что-то другое. В вашем случае, я думаю, вы удалите MockitoJUnitRunnerи сделаете программно то, что он делает.

Фактически единственное, что он делает, это запускает:

MockitoAnnotations.initMocks(test);

в начале тестового примера. Итак, самое простое решение - поместить этот код в setUp()метод:

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

Я не уверен, но, вероятно, вам следует избегать многократного вызова этого метода с использованием флага:

private boolean mockInitialized = false;
@Before
public void setUp() {
    if (!mockInitialized) {
        MockitoAnnotations.initMocks(this);
        mockInitialized = true;  
    }
}

Однако лучше многоразовое решение может быть реализовано с помощью правил JUnt.

public class MockitoRule extends TestWatcher {
    private boolean mockInitialized = false;

    @Override
    protected void starting(Description d) {
        if (!mockInitialized) {
            MockitoAnnotations.initMocks(this);
            mockInitialized = true;  
        }
    }
}

Теперь просто добавьте следующую строку в свой тестовый класс:

@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

и вы можете запустить этот тестовый пример с любым бегуном.

AlexR
источник
13
Проверка mockInitializedневерна. Вы хотите иметь свежий макет для каждого тетста.
BetaRide
1
@BetaRide, это зависит от ваших потребностей. Иногда вы хотите инициализировать mock каждый раз, иногда нет.
AlexR
Если вы хотите настроить один раз для каждого файла класса, вы можете использовать BeforeClass вместо Before, которая будет вызываться один раз и только один раз для каждого тестового файла.
InfernalRapture
56

Начиная с JUnit 4.7 и Mockito 1.10.17, эта функциональность встроена; есть org.mockito.junit.MockitoRuleкласс. Вы можете просто импортировать его и добавить строку

@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

в свой тестовый класс.

Эрика Кейн
источник
2
Для более старых версий Mockito (кажется, до 1.10.5) вы должны использовать:@Rule public MockitoJUnitRule mockito = new MockitoJUnitRule(this);
Cliff Sun
MockitoAnnotations.initMocks(this)очень медленно создает макеты. Самый эффективный способ - использовать @Runwith (MockitoJunitRunner.class)
ant2009 02
16

Это решение работает для всех возможных бегунов, а не только для этого примера mockito. Например; для Spring просто измените классы бегунов и добавьте необходимые аннотации.

@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {

    @Test
    public void subRunner() throws Exception {
        JUnitCore.runClasses(TestMockitoJUnitRunner.class);
    }

    @RunWith(MockitoJUnitRunner.class)
    public static class TestMockitoJUnitRunner {
    }
}

DatabaseModelTestбудет выполняться JUnit. TestMockitoJUnitRunnerзависит от него (по логике) , и он будет работать внутри от основных в @Testметоде, во время вызова JUnitCore.runClasses(TestMockitoJUnitRunner.class). Этот метод гарантирует, что главный бегун будет правильно запущен до запуска static class TestMockitoJUnitRunnerвспомогательного, эффективно реализуя несколько вложенных @RunWithаннотаций с зависимыми тестовыми классами.

Также на https://bekce.github.io/junit-multiple-runwith-dependent-tests

Bekce
источник
3
Звоня JUnitCore.runClasses()без проверки результата, вы рискуете скрыть ошибки из внутреннего теста. assert(JUnitCore.runClasses(TestMockitoJUnitRunner.class).wasSuccessful());хотя бы сообщит вам об ошибке
Роботник
2

В моем случае я пытался издеваться над некоторым методом в Spring bean и

MockitoAnnotations.initMocks(test);

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

...
<bean id="classWantedToBeMocked" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.fullpath.ClassWantedToBeMocked" />
</bean>
...

и добавьте этот bean-компонент с autowired внутри вашего тестового класса, как показано ниже.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="file:springconfig.xml")
public class TestClass {
    ...
    @Autowired
    private ClassWantedToBeMocked classWantedToBeMocked;
    ...
    when(classWantedToBeMocked.methodWantedToBeMocked()).thenReturn(...);
    ...
}
Heungwoo
источник
0

проверьте эту ссылку https://bekce.github.io/junit-multiple-runwith-dependent-tests/, используя этот подход, я объединил @RunWith (Parameterized.class) - внешний бегун - с @RunWith (MockitoJUnitRunner.class) - внутренний бегун. Единственная настройка, которую мне пришлось добавить, - это сделать мои переменные-члены во внешнем классе / бегунке статическими, чтобы сделать их доступными для внутреннего / вложенного бегуна / класса. Удачи и наслаждайся.

Legna
источник
0

Я хотел запустить SWTBotJunit4ClassRunner и org.junit.runners.Parameterized одновременно, у меня есть параметрические тесты, и я хочу делать снимки экрана, когда тест SWT не проходит (функция скриншотов предоставляется SWTBotJunit4ClassRunner ). Ответ @bekce великолепен, и сначала я хотел пойти по этому пути, но это было либо странно, чем аргументы. Или выполнить параметризацию в подклассе и потерять информацию о том, какие именно тесты прошли / не прошли и имеют только последний снимок экрана (поскольку имена снимков экрана получают имя из самого теста). Так что в любом случае это было немного грязно.

В моем случае SWTBotJunit4ClassRunner достаточно прост, поэтому я клонировал исходный код класса, дал ему свое собственное имя ParametrizedScreenshotRunner и там, где оригинал расширял TestRunner , мой класс расширяет класс Parameterized, поэтому по сути я могу использовать свой собственный бегун вместо двух предыдущих. Мой собственный бегун расширяется поверх параметризованного бегуна, реализуя поверх него функцию скриншотов, теперь мой тест использует этот «гибридный» бегун, и все тесты сразу работают, как ожидалось (не нужно ничего менять внутри тестов).

Вот как это выглядит (для краткости я удалил все комментарии из листинга):

package mySwtTests;

import org.junit.runners.Parameterized;
import org.eclipse.swtbot.swt.finder.junit.ScreenshotCaptureListener;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;

public class ParametrizedScreenshotRunner extends TestRu Parameterized {

    public ParametrizedScreenshotRunner(Class<?> klass) throws Throwable {
        super(klass);
    }

    public void run(RunNotifier notifier) {
        RunListener failureSpy = new ScreenshotCaptureListener();
        notifier.removeListener(failureSpy); // remove existing listeners that could be added by suite or class runners
        notifier.addListener(failureSpy);
        try {
            super.run(notifier);
        } finally {
            notifier.removeListener(failureSpy);
        }
    }
}
muni764
источник
-15

Вы также можете попробовать это:

@RunWith(JUnitParamsRunner.class)
public class AbstractTestClass {
  // some tests
}

@RunWith(MockitoJUnitRunner.class)
public class DatabaseModelTest extends AbstractTestClass {
  // some tests
}
Валентин Увегес
источник
2
Это не сработает, будет обработана только аннотация подкласса.
PaulNUK
не работает - только MockitoJUnitRunner аннотации будут приниматься во внимание
Przemek Bielicki