Статьи

TestNG или JUnit

В течение многих лет я всегда возвращался к TestNG, когда дело доходит до модульного тестирования с использованием Java-кода. Каждый раз, когда я выбирал TestNG, люди спрашивали меня, почему я перехожу на TestNG, особенно с JUnit, предоставляемым средой разработки по умолчанию, такой как Eclipse или Maven.

Продолжая то же самое сражение, вчера я начал изучать поддержку тестирования Spring . Он также построен поверх JUnit. Однако, через несколько минут использования того же самого, я искал функцию в JUnit, которую я всегда находил отсутствующей. TestNG обеспечивает параметризованное тестирование с использованием DataProviders. Учитывая, что я снова задавал себе знакомый вопрос — TestNG или JUnit, я решил задокументировать это, чтобы в следующий раз я был уверен, какой и почему.

По сути то же самое

Если вы просто собираетесь провести базовое модульное тестирование, обе платформы в основном одинаковы. Обе платформы позволяют быстро и эффективно тестировать код. У них была поддержка инструментов в Eclipse и других IDE. Они также получили поддержку в таких средах сборки, как Ant и Maven. Для начала JUnit всегда был выбором, потому что это был первый фреймворк для модульного тестирования и всегда был доступен. Многие люди, о которых я говорю, не слышали о TestNG, пока мы не поговорим об этом.

гибкость

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.kapil.itrader;
import java.util.Arrays;
import java.util.List;
import junit.framework.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
 
public class FibonacciTest
{
    private Integer input;
    private Integer expected;
 
    @BeforeClass
    public static void beforeClass()
    {
        // do some initialization
    }
 
    @Test
    public void FibonacciTest()
    {
        System.out.println("Input: " + input + ". Expected: " + expected);
        Assert.assertEquals(expected, Fibonacci.compute(input));
        assertEquals(expected, Fibonacci.compute(input));
    }
}

Ну, это пример демонстрации. Я использую версию 4.x + и использую аннотации. Априори до релиза 4.0; JUnit не поддерживал аннотации, и это было основным преимуществом, которое TestNG имел перед своим конкурентом; но Юнит быстро адаптировался. Вы можете заметить, что JUnit также поддерживает статический импорт, и мы можем покончить с более громоздким кодом, как в предыдущих версиях.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.kapil.framework.core;
import junit.framework.Assert;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
 
public class BaseTestCase
{
    protected static final ClassPathXmlApplicationContext context;
 
    static
    {
        context = new ClassPathXmlApplicationContext("rootTestContext.xml");
        context.registerShutdownHook();
    }
 
    @BeforeSuite
    private void beforeSetup()
    {
       // Do initialization
    }
 
    @Test
    public void testTrue()
    {
        Assert.assertTrue(false);
    }
}

При первом взгляде на два кода можно сделать вывод, что оба кода практически одинаковы. Тем не менее, для тех, кто сделал достаточно модульного тестирования, со мной согласятся, что TestNG обеспечивает большую гибкость. JUnit требует, чтобы я объявил мой метод инициализации как статический; и, следовательно, все, что я напишу в этом методе, должно быть статичным. JUnit также требует, чтобы мой метод инициализации был общедоступным; но TestNG нет. Я также могу использовать лучшие практики ООП в своих классах тестирования. TestNG также позволяет мне объявлять Test Suite, Groups, Methods и использовать аннотации, такие как @BeforeSuite, @BeforeMethod, @BeforeGroups в дополнение к @BeforeClass. Это очень полезно, когда речь идет о написании интеграционных тестов любого уровня или случаев модульного тестирования, которым необходим доступ к общим наборам данных.

Тестирование изоляции и тестирование зависимостей

Junit очень эффективен, когда дело доходит до тестирования в изоляции. По сути, это означает, что вы не можете контролировать порядок выполнения тестов. И, следовательно, если у вас есть два теста, которые вы хотите запустить в определенном порядке из-за какой-либо зависимости, вы не можете сделать это с помощью JUnit. Тем не менее, TestNG позволяет вам сделать это очень эффективно. В Junit вы можете обойти эту проблему, но она не изящна и не так проста.

Тестирование на основе параметров

Очень мощная функция, которую предлагает TestNG — «Параметризованное тестирование». JUnit добавил некоторую поддержку для этого в версиях 4.5+, но это не так эффективно, как TestNG. Возможно, вы работали с FIT, вы бы знали, о чем я говорю. Однако поддержка, добавленная в JUnit, очень проста и не так эффективна. Я изменил свой предыдущий контрольный пример, чтобы включить параметризованное тестирование.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.kapil.itrader;
 
import static org.junit.Assert.assertEquals;
 
import java.util.Arrays;
import java.util.List;
 
import junit.framework.Assert;
 
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
 
@RunWith(Parameterized.class)
public class FibonacciTest
{
    private Integer input;
    private Integer expected;
 
    @Parameters
    public static List data()
    {
        return Arrays.asList(new Integer[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
    }
 
    @BeforeClass
    public static void beforeClass()
    {
        System.out.println("Before");
    }
 
    public FibonacciTest(Integer input, Integer expected)
    {
        this.input = input;
        this.expected = expected;
    }
 
    @Test
    public void FibonacciTest()
    {
        System.out.println("Input: " + input + ". Expected: " + expected);
        Assert.assertEquals(expected, Fibonacci.compute(input));
        assertEquals(expected, Fibonacci.compute(input));
    }
 
}

Вы заметите, что я использовал аннотацию @RunWith для настройки параметров моего тестового примера. В этом случае встроенный метод data (), аннотированный @Parameters, будет использоваться для предоставления данных классу. Однако самая большая проблема заключается в том, что данные передаются в конструктор классов. Это позволяет мне кодировать только логически связанные тестовые случаи в этом классе. И я получу несколько тестовых случаев для одной службы, потому что все различные методы в Службе будут требовать разных наборов данных. Хорошая вещь состоит в том, что есть различные платформы с открытым исходным кодом, которые расширили этот подход и добавили свои собственные реализации «RunWith», чтобы позволить интеграцию с внешними объектами, такими как файлы CSV, HTML или Excel.

TestNG предоставляет эту поддержку из коробки. Не поддержка чтения из CSV или внешних файлов, но из провайдеров данных.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.kapil.itrader.core.managers.admin;
 
import org.testng.Assert;
import org.testng.annotations.Test;
 
import com.uhc.simple.common.BaseTestCase;
import com.uhc.simple.core.admin.manager.ILookupManager;
import com.uhc.simple.core.admin.service.ILookupService;
import com.uhc.simple.dataprovider.admin.LookupValueDataProvider;
import com.uhc.simple.dto.admin.LookupValueRequest;
import com.uhc.simple.dto.admin.LookupValueResponse;
 
/**
 * Test cases to test {@link ILookupService}.
 */
public class LookupServiceTests extends BaseTestCase
{
 
    @Test(dataProvider = "LookupValueProvider", dataProviderClass = LookupValueDataProvider.class)
    public void testGetAllLookupValues(String row, LookupValueRequest request, LookupValueResponse expectedResponse)
    {
        ILookupManager manager = super.getLookupManager();
        LookupValueResponse actualResponse = manager.getLookupValues(request);
        Assert.assertEquals(actualResponse.getStatus(), expectedResponse.getStatus());
    }
}

Приведенный выше фрагмент кода демонстрирует, что я использовал dataProvider в качестве значения для аннотаций, а затем я предоставил класс, который отвечает за создание данных, которые передаются методу во время вызова. Используя этот механизм, я могу легко написать тестовые случаи и их поставщиков данных в виде дуплекса и использовать его очень эффективно.

Почему я выбираю TestNG

Для меня параметризованное тестирование — самая большая причина, почему я выбираю TestNG вместо Junit. Однако все, что я перечислил выше, является причиной, по которой я всегда хочу потратить несколько минут на настройку TestNG в новой настройке Eclipse или проекте maven. TestNG очень полезен для запуска больших наборов тестов. Для небольшого проекта или учебного упражнения хорошо подходит JUnit; потому что любой может начать с этого очень быстро; но не для проектов, где нам нужны тысячи тестовых случаев, и в большинстве этих тестовых случаев у вас будут различные сценарии для покрытия.

Ссылка: TestNG или JUnit от нашего партнера по JCG Капил Вирен Ахаджа в блоге Scratch Pad .