• Stars
    star
    144
  • Rank 255,590 (Top 6 %)
  • Language
    Java
  • License
    Apache License 2.0
  • Created over 7 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

TestNG DataProvider on steroids

Test Data Supplier

Build Status Quality Gate Code Coverage Vulnerabilities Bugs Code Smells Technical Debt Security Rating Maintainability Rating Reliability Rating Maven Central GitHub license Twitter

Table of Contents

Description

This repository contains TestNG DataProvider wrapper (the latest version is based on TestNG 7.7.1) which helps to supply test data in a more flexible way.

Common DataProvider forces using quite old and ugly syntax which expects one of the following types to be returned from DP method's body:

  • Object[][]
  • Iterator<Object[]>

That's weird as developers tend to use Stream and Collection API for data manipulation in the modern Java world.

Just imagine if you could use the following syntax to supply some filtered and sorted data into test method's signature:

@DataSupplier
public Stream<User> getData() {
    return Stream
        .of(
            new User("Max", "password2"),
            new User("Black Fox", "password3"),
            new User("Mark", "password1")
        )
        .filter(u -> !u.getName().contains("Fox"))
        .sorted(comparing(User::getPassword));
}
    
@Test(dataProvider = "getData")
public void shouldSupplyStreamData(final User user) {
    // ...
}

Much better and flexible than two-dimensional arrays or iterators, isn't it?

And what if we don't want to iterate the same test N times depending on collection size? What if we want to inject it into test's signature like the following?

@DataSupplier(transpose = true)
public List<User> getTransposedData() {
    return StreamEx
        .of(
            new User("username1", "password1"),
            new User("username2", "password2")
        )
        .toList();
}
        
@Test(dataProvider = "getTransposedData")
public void shouldSupplyExtractedListData(final List<User> users) {
    // ...
}

Or if you want to extract the values of your collection and inject into test's signature, you can combine transpose with a flatMap:

@DataSupplier(transpose = true, flatMap = true)
public Set<User> getExtractedData() {
    return StreamEx.of("product1", "product2", "product1").toSet();
}
        
@Test(dataProvider = "getExtractedData")
public void shouldSupplyExtractedListData(final String... products) {
    // ...
}

Java-like flatMap operation can be applied even to more complicated structures like Map to extract values for each row:

@DataSupplier(flatMap = true)
public Map<Integer, String> getInternallyExtractedMapData() {
    return EntryStream.of(asList("user3", "user4")).toMap();
}
    
@Test(dataProvider = "getInternallyExtractedMapData")
public void supplyInternallyExtractedMapData(final Integer key, final String value) {
    // not implemented
}

Go top ☝️

Supported flags

  • name: sets a custom name for DataSupplier (method name is used by default)
  • transpose: translates data column into a single row
  • flatMap: behaves pretty much like a native Java Stream operation
  • runInParallel: executes each data-driven test in parallel rather than sequentially
  • indices: filters the underlying collection by given indices
  • propagateTestFailure: fails the test in case of DataSupplier failure (skips by default)

Go top ☝️

Supported return types

  • Collection
  • Map
  • Entry
  • Object[]
  • double[]
  • int[]
  • long[]
  • Stream / StreamEx
  • Tuple
  • A single Object of any common or custom type

Go top ☝️

Usage

Gradle - Java < 9

Add the following configuration into build.gradle:

repositories {
    mavenCentral()
}
    
configurations {
    agent
}

sourceCompatibility = JavaVersion.VERSION_1_8

ext {
    aspectjVersion = '1.9.7'
}

[compileJava, compileTestJava]*.options*.compilerArgs = ['-parameters']
    
dependencies {
    agent "org.aspectj:aspectjweaver:${aspectjVersion}"
    implementation(
            "org.aspectj:aspectjweaver:${aspectjVersion}",
            'org.testng:testng:6.14.3',
            'io.github.sskorol:test-data-supplier:1.7.0'
    )
}
    
test {
    doFirst {
        jvmArgs("-javaagent:${configurations.agent.singleFile}")
    }
    
    useTestNG()
}

Check a separate project with usage examples.

Go top ☝️

Maven - Java < 9

Add the following configuration into pom.xml:

<properties>
    <aspectj.version>1.9.7</aspectj.version>
    <java.version>1.8</java.version>
    <compiler.plugin.version>3.8.0</compiler.plugin.version>
    <surefire.plugin.version>2.20.1</surefire.plugin.version>
</properties>
    
<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>6.14.3</version>
    </dependency>
    <dependency>
        <groupId>io.github.sskorol</groupId>
        <artifactId>test-data-supplier</artifactId>
        <version>1.7.0</version>
    </dependency>
</dependencies>
    
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${compiler.plugin.version}</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
                <compilerArgs>
                    <arg>-parameters</arg>
                </compilerArgs>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>${surefire.plugin.version}</version>
            <configuration>
                <argLine>
                    -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                </argLine>
            </configuration>
        </plugin>
    </plugins>
</build>

Check a separate project with usage examples.

Go top ☝️

Gradle - Java 11-16 w/o modules

plugins {
    id 'java'
}
    
sourceCompatibility = JavaVersion.VERSION_11
    
repositories {
    mavenCentral()
}
    
configurations {
    agent
}

ext {
    aspectjVersion = '1.9.7'
}

[compileJava, compileTestJava]*.options*.compilerArgs = ['-parameters']
    
dependencies {
    agent "org.aspectj:aspectjweaver:${aspetjVersion}"
    implementation(
            "org.aspectj:aspectjweaver:${aspectjVersion}",
            'org.testng:testng:7.4.0',
            'io.github.sskorol:test-data-supplier:1.9.7'
    )
}
    
test {
    doFirst {
        jvmArgs("-javaagent:${configurations.agent.singleFile}")
    }
    
    useTestNG()
}

Go top ☝️

Gradle - Java 11-16 w/ modules

It's a bit tricky in terms of building and testing modular applications:

plugins {
    id 'java-library'
    id 'java'
}
    
ext {
    moduleName = 'your.module.name'
}
    
sourceCompatibility = JavaVersion.VERSION_11
    
repositories {
    mavenCentral()
}
    
configurations {
    agent
}

ext {
    aspectjVersion = '1.9.7'
}
    
dependencies {
    agent "org.aspectj:aspectjweaver:${aspectjVersion}"
    implementation(
            "org.aspectj:aspectjweaver:${aspectjVersion}",
            'org.testng:testng:7.4.0',
            'io.github.sskorol:test-data-supplier:1.9.7'
    )
}
    
compileJava {
    inputs.property("moduleName", moduleName)
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath
        ]
        classpath = files()
    }
}
   
compileTestJava {
    inputs.property("moduleName", moduleName)
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                '--patch-module', "$moduleName=" + files(sourceSets.test.java.srcDirs).asPath,
        ]
        classpath = files()
    }
}
   
test {
    useTestNG()
   
    inputs.property("moduleName", moduleName)
    doFirst {
        jvmArgs = [
                "-javaagent:${configurations.agent.singleFile}",
                '--module-path', classpath.asPath,
                '--add-modules', 'ALL-MODULE-PATH',
                '--add-opens', 'your.module.name/test.package.path=org.testng',
                '--add-opens', 'your.module.name/test.package.path=org.jooq.joor',
                '--patch-module', "$moduleName=" + files(sourceSets.test.java.outputDir).asPath
        ]
        classpath = files()
    }
}

Your module-info.java may look like the following:

module your.module.name {
    requires io.github.sskorol.testdatasupplier;
    requires org.testng;
   
    // Optional
    provides io.github.sskorol.core.IAnnotationTransformerInterceptor
        with path.to.transformer.ImplementationClass;
   
    provides io.github.sskorol.core.DataSupplierInterceptor
        with path.to.interceptor.ImplementationClass;
}

Go top ☝️

Gradle - Java 17+ w/o modules

Note that test-data-supplier:2.0.0+ has been compiled with java 17. It means you must use the same language level in your build file.

plugins {
    id 'java'
}
    
sourceCompatibility = JavaVersion.VERSION_17
    
repositories {
    mavenCentral()
}
    
configurations {
    agent
}

ext {
    aspectjVersion = '1.9.19'
}

[compileJava, compileTestJava]*.options*.compilerArgs = ['-parameters']
    
dependencies {
    agent "org.aspectj:aspectjweaver:${aspectjVersion}"
    implementation(
            "org.aspectj:aspectjweaver:${aspectjVersion}",
            'org.testng:testng:7.7.1',
            'io.github.sskorol:test-data-supplier:2.2.0'
    )
}
    
test {
    doFirst {
        jvmArgs(
            "-javaagent:${configurations.agent.singleFile}",
            '--add-opens', 'java.base/java.lang=ALL-UNNAMED'
        )
    }
    
    useTestNG()
}

Go top ☝️

Maven - Java 17+ w/o modules

<properties>
    <aspectj.version>1.9.19</aspectj.version>
    <java.version>17</java.version>
    <compiler.plugin.version>3.11.0</compiler.plugin.version>
    <surefire.plugin.version>3.0.0-M9</surefire.plugin.version>
</properties>
    
<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>7.7.1</version>
    </dependency>
    <dependency>
        <groupId>io.github.sskorol</groupId>
        <artifactId>test-data-supplier</artifactId>
        <version>2.2.0</version>
    </dependency>
</dependencies>
    
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${compiler.plugin.version}</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
                <compilerArgs>
                    <arg>-parameters</arg>
                </compilerArgs>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>${surefire.plugin.version}</version>
            <configuration>
                <argLine>
                    -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                </argLine>
            </configuration>
        </plugin>
    </plugins>
</build>

Go top ☝️

API

Instead of a common DataProvider annotation use the following:

@DataSupplier
public T getData() {
    //...
}

DataSupplier supports the following args: name, transpose, flatMap, runInParallel, indices and propagateTestFailure.

You can refer DataSupplier the same way as with TestNG DataProvider:

@Test(dataProvider = "getData")
public void supplyData(final T data) {
    // ...
}
    
// or
    
@Test(dataProviderClass = ExternalDataProvider.class, dataProvider = "getData")
public void supplyExternalData(final T data) {
    // ...
}

Check io.github.sskorol.testcases package for more examples.

Go top ☝️

JSON, CSV, YAML and XLSX processors

Test data supplier supports JSON, CSV, YML and XLSX data retrieval. Assuming you have the following resources:

username,password
"admin","admin"
"sskorol","password"
"guest","123"
[
  {
    "username": "admin",
    "password": "admin"
  },
  {
    "username": "sskorol",
    "password": "password"
  },
  {
    "username": "guest",
    "password": "123"
  }
]
---
 username: admin
 password: admin
---
 username: sskorol
 password: password
---
 username: guest
 password: '123'
USERNAME  PASSWORD
admin     admin
sskorol   password
guest     123

You can now map Java entities to these data sources using @Source annotation, which accepts either local file name or URL:

@Data
@Source(path = "users.csv")
public class User {
    @FieldName("username")
    private final String name;
    private final String password;
}
@Data
@Source(path = "users.json")
public class User {
    @SerializedName("username")
    private final String name;
    private final String password;
}
@Data
@NoArgsConstructor
@Source(path = "users.yml")
public class User {
    @JsonProperty("username")
    private final String name;
    private final String password;
}
@Data
@NoArgsConstructor
@Source(path = "users.xlsx")
@Sheet(name = "sheet_1")
public class User {
    @Column(name = "USERNAME")
    private String username;

    @Column(name = "PASSWORD")
    private String password;
}

In case if some Java field's name differs from its data source representation, you can assign a valid name via @FieldName for CSV, @SerializedName for JSON and @JsonProperty for YML data type.

Excel support is experimental. 2.0.0 version used ZeroCell library based on Apache POI to simplify corresponding files processing. Since 2.1.0, there's a custom implementation with similar approach, but minor improvements, e.g. there's no need to use column index anymore.

In terms of fields' mapping, you can use custom @Column annotation (don't confuse with ZeroCell Column). You should also make sure you provided a sheet name via corresponding @Sheet annotation. Otherwise, the first one will be used.

Similarly to ZeroCell, you can use either default or custom fields' converters. Here's a list of defaults:

  • BooleanConverter
  • DoubleConverter
  • IntegerConverter
  • LocalDateConverter
  • LocalDateTimeConverter
  • StringConverter

To use custom converter, you should specify its class via @Column annotation.

@Column(name = "Tags", converter = StringToListConverter.class)
private List<String> data;

And the actual implementation may look like the following:

public class StringToListIConverter extends DefaultConverter<List<String>> {
    @Override
    public List<String> convert(final String value) {
        return asList(value.split(","));
    }
}

Custom converters must extend DefaultConverter class. Also note that by default test-data-supplier uses an implicit conversion based on the field type. So you don't have to explicitly specify a converter if it's among the defaults.

Local data sources must be located in a classpath. You usually use resources folder for that.

Then, within @DataSupplier you can call a special TestDataReader builder to retrieve data from CSV, JSON, YML or XLSX data source. See javadocs to get more details.

@DataSupplier
public StreamEx<User> getUsers() {
    return use(CsvReader.class).withTarget(User.class).withSource("users.csv").read();
}
@DataSupplier
public StreamEx<User> getUsers() {
    return use(JsonReader.class).withTarget(User.class).withSource("http://users.json").read();
}
@DataSupplier
public StreamEx<User> getUsers() {
    return use(YamlReader.class).withTarget(User.class).read();
}
@DataSupplier
public StreamEx<User> getUsers() {
    return use(XlsxReader.class).withTarget(User.class).read();
}

If you want to specify a custom source in runtime, you can remove @Source annotation and use withSource builder method instead.

Note that in case of a data reading error or any kind of exception thrown in a @DataSupplier body, the corresponding test will be skipped. That's a default TestNG behaviour. However, you can set propagateTestFailure flag (introduced in TestNG 7.6.0) to explicitly mark the test as failed.

Go top ☝️

DB support

Technically, there's no need to create an additional ORM wrapper to work with databases. But it's worth to show how to perform such integration.

Let's use ebean and Postgres as an example.

First, create an application-test.yaml with db connection details in your test resources' folder:

ebean:
  test:
    useDocker: false
    platform: postgres
    ddlMode: none
    dbName: your_db
    dbSchema: your_schema
    postgres:
      username: your_username
      password: your_password
      url: jdbc:postgresql://localhost:5432/your_db

Next, create a mapping with your DB table:

@MappedSuperclass
public class BaseEntity extends Model {
    @Id
    long id;
}

@Entity
@Table(name = "testing.users")
public class UserEntity extends BaseEntity {
    @NotNull
    public String email;

    @Column(name = "is_active")
    public boolean isActive;
}

Now, you can supply users to your test the following way:

@DataSupplier
public List<UserEntity> usersData() {
    return find(UserEntity.class)
        .where()
        .like("email", "%korol%@gmail.com")
        .and()
        .eq("is_active", true)
        .findList();
}

Go top ☝️

Factory

You can specify DataSupplier for Factory annotation as well as for common test methods.

@NoArgsConstructor
public class InternalFactoryTests {
    
    @DataSupplier
    public StreamEx getConstructorData() {
        return IntStreamEx.rangeClosed(1, 3).boxed();
    }
    
    @DataSupplier
    public String getTestData() {
        return "data";
    }
    
    @Factory(dataProvider = "getConstructorData", dataProviderClass = InternalFactoryTests.class)
    public InternalFactoryTests(final int index) {
        // not implemented
    }
    
    @Test(dataProvider = "getTestData")
    public void internalFactoryTest(final String data) {
        // not implemented
    }
}

Go top ☝️

Tracking meta-data

DataSupplierInterceptor interface allows tracking original DataProvider method calls for accessing additional meta-data. You can use the following snippet for getting required info:

public class DataSupplierInterceptorImpl implements DataSupplierInterceptor {
    
    private static final Map<ITestNGMethod, DataSupplierMetaData> META_DATA = new ConcurrentHashMap<>();
    
    @Override
    public void beforeDataPreparation(final ITestContext context, final ITestNGMethod method) {
    }
    
    @Override
    public void afterDataPreparation(final ITestContext context, final ITestNGMethod method) {
    }
    
    @Override
    public void onDataPreparation(final DataSupplierMetaData testMetaData) {
        META_DATA.putIfAbsent(testMetaData.getTestMethod(), testMetaData);
    }
    
    @Override
    public Collection<DataSupplierMetaData> getMetaData() {
        return META_DATA.values();
    }
}

This class should be then loaded via SPI mechanism. Just create META-INF/services folder in resources root, and add a new file io.github.sskorol.core.DataSupplierInterceptor with a full path to implementation class.

Go top ☝️

IAnnotationTransformer restriction

TestNG restricts users in a number of IAnnotationTransformer implementations. You may have not more than a single transformer within project's scope. As test-data-supplier uses this interface for its internal stuff, you won't be able to apply your own implementation.

In case if you still need to add a custom IAnnotationTransformer, you have to implement the following interface:

public class IAnnotationTransformerInterceptorImpl implements IAnnotationTransformerInterceptor {

    @Override
    public void transform(IFactoryAnnotation annotation, Method testMethod) {
    }

    @Override
    public void transform(IConfigurationAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
    }

    @Override
    public void transform(IDataProviderAnnotation annotation, Method method) {
    }

    @Override
    public void transform(IListenersAnnotation annotation, Class testClass) {
    }
}

It's just an SPI wrapper for common TestNG feature. Use the same technique as for DataSupplierInterceptor to include it into your project.

Note that in case if you want to manage DataProviderTransformer manually, you have to use a special spi-off distribution:

dependencies {
    implementation 'io.github.sskorol:test-data-supplier:2.2.0:spi-off'
}

Go top ☝️

IntelliJ IDEA support

Test Data Supplier also has an IntelliJ IDEA plugin. Just install test-data-supplier-plugin from the official JetBrains repository.

More information about its features can be found on the related GitHub page.

Go top ☝️

More Repositories

1

ts-test-decorators

Write your tests in a Java-like annotation-driven manner via JS decorators
TypeScript
40
star
2

ros2-humble-docker-dev-template

This repo provides a template for a quick start with a ROS2 Humble development in Docker
Dockerfile
38
star
3

vosk-api-gpu

Vosk ASR Docker images with GPU for Jetson boards, PCs, M1 laptops and GPC
Shell
37
star
4

web-automation-framework

Extensive samples of creating custom factories and code prettification using new Java 8 features
Java
20
star
5

mocha-allure2-example

Allure 2 Mocha examples
TypeScript
18
star
6

docker-selenium-samples

Basic selenium test samples with video recording support.
Java
16
star
7

selenium-camp-17

Source code for SC17 talk: http://seleniumcamp.com/talk/how-java-8-can-simplify-test-automation
Java
15
star
8

webdriver-supplier

A small library which helps managing web browsers' lifecycle.
Java
13
star
9

allure2-testng-report

Sample project for Allure2 report generation
Java
12
star
10

alexa-smart-home-skill-template

A sample Alexa Smart Home Skill which could be used as a baseline for any similar project.
TypeScript
11
star
11

selenium-camp-16

Selenium Camp 2016 - Effective UI tests scaling on Java - talk's demo
Java
9
star
12

docker-selenium-grid

Custom selenium server tweaks to support video recording within docker containers
Java
9
star
13

ner-spacy-doccano

NER using Doccano / Spacy EN
Python
8
star
14

xgo_ros

ROS2 Humble port of the official ROS1 controller provided by XGO team
C++
8
star
15

minipupper-teleop

MiniPupper robot teleoperation via ROS and WebRTC
JavaScript
8
star
16

qaa-amazon

Home task for the next 2 weeks.
Java
7
star
17

wdio-testdeck-allure-examples

Decorators'-based WDIO examples.
TypeScript
6
star
18

docker-browsermob-proxy

Docker image for Browsermob Proxy
Shell
5
star
19

mocha-allure2-reporter

Allure 2 reporter for Mocha framework
TypeScript
5
star
20

respeaker-websockets

This project reveals full Respeaker Core V2 potential by using bundled Alango DSP algorithms for audio pre-processing before sending to the custom ASR server.
C++
5
star
21

browsermob-proxy-rest

REST client for Browsermob Proxy
Java
3
star
22

aqa-playground

Comprehensive testbed designed primarily for passionate SDETs to practice their skills in real-world scenarios
TypeScript
3
star
23

alexa-middleware

Sample Middleware service for routing requests between Alexa and micro-controllers.
TypeScript
3
star
24

test-automation-playground

ReactJS application with Spring Boot / MySQL backend created for practicing automation skills.
JavaScript
3
star
25

select2-wrapper

Java
2
star
26

arduino-alexa-bridge

A basic bridge between Arduino / ESP32 and Alexa
C++
2
star
27

test-data-supplier-maven-example

A basic sample, which demonstrates test-data-supplier library usage
Java
2
star
28

rae-scripts

A set of scripts that help with different RAE issues resolution
Shell
2
star
29

arduino-sensors-wrappers

A set of simple wrappers for common Arduino sensors
C++
1
star
30

matrix-voice-esp32-ws-streamer

Matrix Voice streaming via WebSockets.
C++
1
star
31

test-data-supplier-gradle-example

A basic sample, which demonstrates test-data-supplier library usage
Java
1
star
32

selenium-utils

Java
1
star
33

selenium-camp-18

Source code for Selenium Camp 18 talk: https://seleniumcamp.com/talk/the-essential-tools-for-test-code-quality-improvement
1
star
34

facebook-sample

Just a simple interview task
Java
1
star
35

reportng-tests

Java
1
star