Огурец: бэкэнд не обнаружен при запуске из Spring Boot jar

Я создаю небольшую среду тестирования, которая должна использовать как Cucumber, так и платформу Spring Boot. Идея состоит в том, чтобы все приложение было упаковано в одну банку и запускалось после того, как функции BDD были правильно параметризованы.

Фреймворк запускается в режиме запуска командной строки следующим образом:

public class FwApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(FwApplication.class, args);
    }

    @Override
    public void run(String... arg0) throws Exception {
        JUnitCore.main(CucumberIntegration.class.getCanonicalName());
    }
}

Затем есть класс CucumberIntegration:

@RunWith(Cucumber.class)
@CucumberOptions(features = "config/features")
@ContextConfiguration(classes= AppConfiguration.class)
public class CucumberIntegration {
}

У меня также есть несколько простых тестов, которые отлично работают в моей среде IDE, но когда я пытаюсь упаковать приложение и запустить его через java -jar fw-0.0.1-SNAPSHOT.jar, я вижу следующее:

There was 1 failure:
1) initializationError(com.fmr.bddfw.test.CucumberIntegration)
cucumber.runtime.CucumberException: No backends were found. Please make sure you have a backend module on your CLASSPATH.
        at cucumber.runtime.Runtime.<init>(Runtime.java:81)
        at cucumber.runtime.Runtime.<init>(Runtime.java:70)
        (...)

Все необходимые банки уже находятся в одной банке, созданной maven, и она отлично работает в моей среде IDE.

Есть идеи, что могло бы помочь?

РЕДАКТИРОВАТЬ: вот мой файл pom.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xxx</groupId>
    <artifactId>fw</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>fw</name>
    <description>BDD Testing Framework</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <cucumber.version>1.2.5</cucumber.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>${cucumber.version}</version>
        </dependency>

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-spring</artifactId>
            <version>${cucumber.version}</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

person Marek Puchalski    schedule 19.05.2017    source источник
comment
Вы должны добавить зависимость огурец-java   -  person Issam El-atif    schedule 19.05.2017
comment
Похоже, что это не так. cucumber-spring зависит от cucumber-java. Банки включены. И это работает в IDE. : /   -  person Marek Puchalski    schedule 19.05.2017
comment
Извините, что воскресил этот пост, но вы когда-нибудь находили решение? Я не могу найти способ сделать загрузчик ресурсов, сохраняя при этом отдельные тесты. В том виде, в каком они сейчас стоят, я могу запустить каждую из них в своей среде IDE. Я бы не хотел все ломать.   -  person Saita    schedule 18.09.2019


Ответы (5)


Используя предложение, данное Маркусом:

Шаг 1. Создайте собственный класс MultiLoader:

package cucumber.runtime.io;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

public class CustomMultiLoader implements ResourceLoader {
    public static final String CLASSPATH_SCHEME = "classpath*:";
    public static final String CLASSPATH_SCHEME_TO_REPLACE = "classpath:";
    private final ClasspathResourceLoader classpath;
    private final FileResourceLoader fs;
    public CustomMultiLoader(ClassLoader classLoader) {
        classpath = new ClasspathResourceLoader(classLoader);
        fs = new FileResourceLoader();
    }
    @Override
    public Iterable<Resource> resources(String path, String suffix) {
        if (isClasspathPath(path)) {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            String locationPattern = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME) + "/**/*" + suffix;
            org.springframework.core.io.Resource[] resources;
            try {
                resources = resolver.getResources(locationPattern);
            } catch (IOException e) {
                resources = null;
                e.printStackTrace();
            }
            return convertToCucumberIterator(resources);
        } else {
            return fs.resources(path, suffix);
        }
    }
    private Iterable<Resource> convertToCucumberIterator(org.springframework.core.io.Resource[] resources) {
        List<Resource> results = new ArrayList<Resource>();
        for (org.springframework.core.io.Resource resource : resources) {
            results.add(new ResourceAdapter(resource));
        }
        return results;
    }

    public static String packageName(String gluePath) {
        if (isClasspathPath(gluePath)) {
            gluePath = stripClasspathPrefix(gluePath);
        }
        return gluePath.replace('/', '.').replace('\\', '.');
    }

    private static boolean isClasspathPath(String path) {
        if (path.startsWith(CLASSPATH_SCHEME_TO_REPLACE)) {
            path = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME);
        }
        return path.startsWith(CLASSPATH_SCHEME);
    }

    private static String stripClasspathPrefix(String path) {
        if (path.startsWith(CLASSPATH_SCHEME_TO_REPLACE)) {
            path = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME);
        }
        return path.substring(CLASSPATH_SCHEME.length());
    }

}

Шаг 2. Создайте адаптер между org.springframework.core.io.Resource и cucumber.runtime.io.Resource:

package cucumber.runtime.io;

import java.io.IOException;
import java.io.InputStream;

public class ResourceAdapter implements Resource {
    org.springframework.core.io.Resource springResource;

    public ResourceAdapter(org.springframework.core.io.Resource springResource) {
        this.springResource = springResource;
    }

    public String getPath() {
        try {
            return springResource.getFile().getPath();
        } catch (IOException e) {
            try {
                return springResource.getURL().toString();
            } catch (IOException e1) {
                e1.printStackTrace();
                return "";
            }
        }
    }

    public String getAbsolutePath() {
        try {
            return springResource.getFile().getAbsolutePath();
        } catch (IOException e) {
            return null;
        }
    }

    public InputStream getInputStream() throws IOException {
        return this.springResource.getInputStream();
    }

    public String getClassName(String extension) {

        String path = this.getPath();
        if (path.startsWith("jar:")) {
            path = path.substring(path.lastIndexOf("!") + 2);
            return path.substring(0, path.length() - extension.length()).replace('/', '.');
        } else {
            path = path.substring(path.lastIndexOf("classes") + 8);
            return path.substring(0, path.length() - extension.length()).replace('\\', '.');
        }

    }
}

Шаг 3. Создайте собственный основной класс, который использует CustomMultiLoader:

package cucumber.runtime.io;

import static java.util.Arrays.asList;

import java.io.IOException;
import java.util.ArrayList;

import cucumber.runtime.ClassFinder;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeOptions;

public class CucumberStaticRunner {

    public static void startTests(String[] argv) throws Throwable {
        byte exitstatus = run(argv, Thread.currentThread().getContextClassLoader());
        System.exit(exitstatus);
    }

    public static byte run(String[] argv, ClassLoader classLoader) throws IOException {
        RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList(argv)));

        ResourceLoader resourceLoader = new CustomMultiLoader(classLoader);
        ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
        Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
        runtime.run();
        return runtime.exitStatus();
    }
}

Шаг 4. Вызовите собственный основной класс вместо cucumber.api.cli.Main.main:

String[] cucumberOptions = { "--glue", "my.test.pack", "--no-dry-run", "--monochrome", "classpath:features" };
CucumberStaticRunner.startTests(cucumberOptions);
person SomeDev2017    schedule 04.10.2017
comment
Я знаю, что прошло много времени, но не могли бы вы помочь мне разобраться в этом в текущей версии (4.7.1) библиотеки огурцов? Я разместил его на stackoverflow.com/questions/57995598/ - person Saita; 19.09.2019
comment
Я выполнил 4 шага, упомянутых выше, в моем приложении загрузки sping, теперь оно может загружать файл .feature и определения шагов. Но проблема в java-файлах определения шага. Я автоматически подключил несколько свойств из application.properties, которые не работают. не могли бы вы помочь мне в этом. - person Raju; 05.12.2019

Исправлено с помощью следующей конфигурации:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
       <requiresUnpack>
           <dependency>
                 <groupId>info.cukes</groupId>
                 <artifactId>cucumber-java</artifactId>
          </dependency>
       </requiresUnpack>
    </configuration>
</plugin>
person Murphy Lan    schedule 10.11.2017

Вы должны добавить зависимость огурец-java

<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>${cucumber.version}</version>
</dependency>
person Issam El-atif    schedule 19.05.2017
comment
Нет, в этом нет необходимости. cucumber-spring зависит от cucumber-java. - person Marek Puchalski; 19.05.2017

Я использовал код ниже в файле POM.xml

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>1.2.4</version>
            <scope>test</scope>
        </dependency>

Теперь он работает нормально.

person Arun Salvi QA    schedule 31.12.2018

person    schedule
comment
Не могли бы вы дать еще контекст? Как это можно использовать в случае OP (Spring Boot + JUnit)? - person Saita; 18.09.2019