我面临一个问题,单元测试用例通过了IntelliJ,但在mvn test命令上失败了.我认为问题出在Java 17上,但我不确定.SRC代码可以找到here

Maven版本(带有JDK 17):

Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: D:\Apps\maven-3.6.3\bin\..
Java version: 17.0.1, vendor: Oracle Corporation, runtime: D:\Program Files\jdk17\app
Default locale: en_US, platform encoding: Cp1258
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

-pom.xml:

<properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.google.inject/guice -->
    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>5.1.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.13.3</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.3</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
    <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.14.3</version>
    </dependency>

    <!-- Test Dependencies -->
    <!-- https://mvnrepository.com/artifact/com.google.truth/truth -->
    <dependency>
        <groupId>com.google.truth</groupId>
        <artifactId>truth</artifactId>
        <version>1.1.3</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.8.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <configuration>
                        <archive>
                            <manifest>
                                <mainClass>
                                    com.udacity.webcrawler.main.WebCrawlerMain
                                </mainClass>
                            </manifest>
                        </archive>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                        <appendAssemblyId>false</appendAssemblyId>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <trimStackTrace>false</trimStackTrace>
                <systemProperties>
                    <property>
                        <name>testDataDir</name>
                        <value>${project.basedir}/src/test/data</value>
                    </property>
                    <property>
                        <name>crawlerImplementations</name>
                        <value>
                            com.udacity.webcrawler.SequentialWebCrawler
                            com.udacity.webcrawler.ParallelWebCrawler
                        </value>
                    </property>
                </systemProperties>
            </configuration>
            <version>3.1.0</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-engine</artifactId>
                    <version>5.8.2</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

以下是代码:

public final class ProfilerImplTest {
    private final FakeClock clock = new FakeClock();
    private final Profiler profiler = new ProfilerImpl(clock, true);
    private final ProfiledInterfaceImpl delegate = new ProfiledInterfaceImpl(clock);

    @Test
    public void delegateHasNoMethodsAnnotated() {
        assertThrows(
                IllegalArgumentException.class,
                () -> profiler.wrap(NonProfiledInterface.class, new NonProfiledInterfaceImpl()),
                "Profiler.wrap() should throw an IllegalArgumentException if the wrapped interface does " +
                        "not contain a @Profiled method.");
    }

    @Test
    public void testToString() {
        ProfiledInterface proxy = profiler.wrap(ProfiledInterface.class, delegate);

        assertWithMessage("The proxy should delegate toString() calls to the wrapped object.")
                .that(proxy.toString())
                .isEqualTo(delegate.toString());
    }

    @Test
    public void testHashCode() {
        ProfiledInterface proxy = profiler.wrap(ProfiledInterface.class, delegate);

        assertWithMessage("The proxy should delegate hashCode() calls to the wrapped object.")
                .that(proxy.hashCode())
                .isEqualTo(delegate.hashCode());
    }

    @Test
    public void testEquals() {
        ProfiledInterface proxy1 = profiler.wrap(ProfiledInterface.class, delegate);
        ProfiledInterface proxy2 = profiler.wrap(ProfiledInterface.class, delegate);

        assertThat(proxy1).isNotSameInstanceAs(delegate);
        assertThat(proxy1).isEqualTo(delegate);
        assertThat(delegate).isEqualTo(proxy1);

        assertWithMessage("Each call to Profiler.wrap() should create a new proxy object.")
                .that(proxy1)
                .isNotSameInstanceAs(proxy2);
        assertWithMessage("Two proxies should be equal if their wrapped objects are equal")
                .that(proxy1)
                .isEqualTo(proxy2);
        assertWithMessage("Two proxies should be equal if their wrapped objects are equal")
                .that(proxy2)
                .isEqualTo(proxy1);
    }

    @Test
    public void testNonObjectEquals() {
        ProfiledInterface proxy = profiler.wrap(ProfiledInterface.class, delegate);

        assertWithMessage("Incorrect equals() method was called")
                .that(proxy.equals("foo", "bar"))
                .isFalse();

        assertThat(delegate.wasFakeEqualsCalled()).isTrue();
    }

    @Test
    public void testBasicProfiling() throws Exception {
        ProfiledInterface proxy = profiler.wrap(ProfiledInterface.class, delegate);

        Instant beforeInvocation = clock.instant();

        assertWithMessage("The intercepted method did not forward the return value correctly")
                .that(proxy.profiled())
                .isEqualTo("profiled");
        Instant afterInvocation = clock.instant();
        assertWithMessage("Expected time to advance from invocation.")
                .that(beforeInvocation)
                .isLessThan(afterInvocation);

        // Run the method again a few more times to aggregate some data.
        proxy.profiled();
        proxy.profiled();

        CloseableStringWriter writer = new CloseableStringWriter();
        profiler.writeData(writer);
        assertWithMessage("Streams should usually be closed in the same scope where they were created")
                .that(writer.isClosed())
                .isFalse();
        String written = writer.toString();
        assertWithMessage("The profile data was not written or is incorrect")
                .that(written)
                .contains(
                        "com.udacity.webcrawler.profiler.ProfilerImplTest$ProfiledInterfaceImpl#profiled");
        assertThat(written).contains("0m 3s 0ms");
    }

    @Test
    public void testDeclaredExceptionHandling() throws Exception {
        ProfiledInterface proxy = profiler.wrap(ProfiledInterface.class, delegate);

        Instant beforeInvocation = clock.instant();
        Throwable expected = assertThrows(
                Throwable.class,
                () -> proxy.throwSomething(new Throwable("expected exception")),
                "The method interceptor should forward exceptions thrown by the wrapped object");
        assertWithMessage("The proxy threw a different exception than was thrown by the wrapped object")
                .that(expected)
                .hasMessageThat()
                .isEqualTo("expected exception");

        Instant afterInvocation = clock.instant();
        assertWithMessage("Expected time to advance from invocation.")
                .that(beforeInvocation)
                .isLessThan(afterInvocation);

        CloseableStringWriter writer = new CloseableStringWriter();
        profiler.writeData(writer);
        assertWithMessage("Streams should usually be closed in the same scope where they were created")
                .that(writer.isClosed())
                .isFalse();
        String written = writer.toString();
        assertWithMessage("Profile data should still be recorded if an exception was thrown.")
                .that(written)
                .contains("com.udacity.webcrawler.profiler.ProfilerImplTest$ProfiledInterfaceImpl");
        assertThat(written).contains("0m 1s 0ms");
    }

    @Test
    public void testServiceLoader() throws Exception {
        Field metadataField = ProfilerImpl.class.getDeclaredField("serviceMetadata");
        metadataField.setAccessible(true);
        Profiler trueProfiler = new ProfilerImpl(clock, true);
        Profiler falseProfiler = new ProfilerImpl(clock, false);

        assertEquals(2, ((Map<Class<?>, ServiceMetadata<?>>) metadataField.get(falseProfiler)).size());
        assertEquals(3, ((Map<Class<?>, ServiceMetadata<?>>) metadataField.get(trueProfiler)).size());
    }

    /**
     * A test interface that does not have any {@link Profiled} methods.
     */
    private interface NonProfiledInterface {
    }

    /**
     * Concrete implementation of {@link NonProfiledInterface}.
     */
    private static final class NonProfiledInterfaceImpl implements NonProfiledInterface {
    }

    /**
     * A test interface that has a method annotated with {@link Profiled}.
     */
    private interface ProfiledInterface {
        @Profiled
        String profiled();

        @Profiled
        void throwSomething(Throwable throwable) throws Throwable;

        boolean equals(String foo, String bar);
    }

    /**
     * Concrete implementation of {@link ProfiledInterface}.
     */
    @Wrapped
    private static final class ProfiledInterfaceImpl implements ProfiledInterface {
        private final FakeClock fakeClock;
        private boolean wasFakeEqualsCalled = false;

        ProfiledInterfaceImpl(FakeClock fakeClock) {
            this.fakeClock = Objects.requireNonNull(fakeClock);
        }

        @Override
        public String profiled() {
            fakeClock.tick(Duration.ofSeconds(1));
            return "profiled";
        }

        @Override
        public void throwSomething(Throwable throwable) throws Throwable {
            fakeClock.tick(Duration.ofSeconds(1));
            throw throwable;
        }

        @Override
        public boolean equals(Object other) {
            // All instances of ProfiledInterface are equal to one another.
            return (other instanceof ProfiledInterface);
        }

        @Override
        public boolean equals(String foo, String bar) {
            Objects.requireNonNull(foo);
            Objects.requireNonNull(bar);
            wasFakeEqualsCalled = true;
            return false;
        }

        public boolean wasFakeEqualsCalled() {
            return wasFakeEqualsCalled;
        }
    }
}

在第private final Profiler profiler = new ProfilerImpl(clock, true);行抛出异常,我不知道为什么.请判断以下代码:

/**
 * Concrete implementation of the {@link Profiler}.
 */
final class ProfilerImpl implements Profiler {
    private final Map<Class<?>, ServiceMetadata<?>> serviceMetadata;
    private final Clock clock;
    private final ProfilingState state = new ProfilingState();
    private final ZonedDateTime startTime;

    @Inject
    ProfilerImpl(Clock clock, boolean includeTest) {
        this.clock = Objects.requireNonNull(clock);
        this.startTime = ZonedDateTime.now(clock);
        ServiceLocator<Class<?>> serviceLocator = ServiceLocator.webCrawlerLocator(includeTest);
        serviceMetadata = serviceLocator.parse(serviceLocator.locateService());
    }

    private ServiceMetadata<?> profiledClass(Class<?> klass) {
        for (Class<?> clazz : serviceMetadata.keySet()) {
            if (klass.isAssignableFrom(clazz)) {
                return serviceMetadata.get(clazz);
            }
        }
        return null;
    }

    @Override
    public <T> T wrap(Class<T> klass, T delegate) {
        Objects.requireNonNull(klass);

        ServiceMetadata<?> profiledClass = profiledClass(klass);
        if (profiledClass == null) {
            throw new IllegalArgumentException(klass.getName() + "doesn't have profiled methods.");
        }

        ProfilingMethodInterceptor interceptor = new ProfilingMethodInterceptor(clock, delegate, state, startTime, profiledClass);

        Object proxy = Proxy.newProxyInstance(
                ProfilerImpl.class.getClassLoader(),
                new Class[]{klass},
                interceptor
        );

        return (T) proxy;
    }

    @Override
    public void writeData(Path path) {
        Objects.requireNonNull(path);

        try (Writer writer = Files.newBufferedWriter(path, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
            writeData(writer);
            writer.flush();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void writeData(Writer writer) throws IOException {
        writer.write("Run at " + RFC_1123_DATE_TIME.format(startTime));
        writer.write(System.lineSeparator());
        state.write(writer);
        writer.write(System.lineSeparator());
    }
}

堆栈跟踪:

Caused by: java.lang.ClassNotFoundException: target.classes.com.udacity.webcrawler.IgnoredUrls
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
        at com.google.common.reflect.ClassPath$ClassInfo.load(ClassPath.java:360)
        ... 88 more

请帮帮忙

推荐答案

在对问题I的 comments 中的行动之后:

  • WebCrawlerServiceLocator的(第52行)filter(classInfo -> ...)之前增加了.peek(System.out::println)
  • Ran MVN测试 -Dmaven.surefire.debug > mvn-test.out
  • 其中第5617行有target.classes.com.udacity.webcrawler.json.ConfigurationLoader个,后面是问题及其 comments 中提到的错误:
...
target.classes.com.udacity.webcrawler.json.ConfigurationLoader
[ERROR] Tests run: 8, Failures: 0, Errors: 8, Skipped: 0, Time elapsed: 0.84 s <<< FAILURE! - in com.udacity.webcrawler.profiler.ProfilerImplTest
[ERROR] delegateHasNoMethodsAnnotated  Time elapsed: 0.008 s  <<< ERROR!
java.lang.ExceptionInInitializerError
    at com.udacity.webcrawler.service.ServiceLocator.webCrawlerLocator(ServiceLocator.java:13)
    at com.udacity.webcrawler.profiler.ProfilerImpl.<init>(ProfilerImpl.java:34)
    at com.udacity.webcrawler.profiler.ProfilerImplTest.<init>(ProfilerImplTest.java:27)
    ...

因此,显然ImmutableSet<ClassInfo> com.google.common.reflect.ClassPath.getAllClasses()将Maven的${project.basedir}作为类路径条目.问题是:

不管怎么说,开头提到了ClassPath's javadoc次:

Prefer ClassGraph over ClassPath

更新

出于好奇,我实现了ClassGraph的用法:

POM

        ...
        <dependency>
            <groupId>io.github.classgraph</groupId>
            <artifactId>classgraph</artifactId>
            <version>4.8.160</version>
        </dependency>
        ...

WebCrawlerServiceLocator

...
import static java.lang.System.out;
...
import io.github.classgraph.ClassGraph;
...
private static final Class<? extends Annotation> WRAPPED = Wrapped.class;
...
    @Override
    public Collection<Class<?>> locateService() {

        try {
            return new HashSet<>( ClassPath.from( ClassLoader.getSystemClassLoader() ).getAllClasses().stream()
                    .filter( classInfo -> !classInfo.getName().equals( "module-info" ) || !classInfo.getName().equals( "package-info" ) )
                    //.peek( out::println )
                    .map( classInfo -> {
                        try {

                            //if ( classInfo.getName().contains( "com.udacity" ) )
                            //  out.printf( "--> name: %s%n--> cp: %s%n%n",
                            //      classInfo.getName(), System.getProperty( "java.class.path" ) );

                            return classInfo.load();
                        }
                        catch ( Error | Exception e ) {
                            out.printf( "--> %s --> return null%n", e );
                            return null;
                        }
                    } ) // map(...)
                    //.peek( out::println )
                    .filter( Objects::nonNull )
                    .filter( clazz -> clazz.isAnnotationPresent( WRAPPED ) )
                    .toList() ); // HashSet<>(...)
        }
        catch ( IOException e ) {
            out.printf( "--> %s%n--> return empty list%n", e );
            return Collections.emptyList();
        }
    }

    public Collection<Class<?>> locateService2() {

        try {
            return new HashSet<Class<?>>(
                    new ClassGraph().enableAllInfo().scan().getAllClasses().stream()
                            .filter( classInfo -> !classInfo.getName().equals( "module-info" ) || !classInfo.getName().equals( "package-info" ) )
                            //.peek( out::println )
                            .filter( classInfo -> classInfo.hasAnnotation( WRAPPED ) )
                            .map( classInfo -> {
                                try {

                                    //if ( classInfo.getName().contains( "com.udacity" ) )
                                    //  out.printf( "--> name: %s%n--> cp: %s%n%n",
                                    //      classInfo.getName(), System.getProperty( "java.class.path" ) );

                                    return classInfo.loadClass();
                                }
                                catch ( Error | Exception e ) {
                                    out.printf( "--> %s --> return null%n", e );
                                    return null;
                                }
                            } ) // map(...
                            //.peek( out::println )
                            .filter( Objects::nonNull )
                            .toList() ); // HashSet<>(...)
        }
        // with IOException compile error: "Unreachable catch block for IOException. This exception is never thrown from the try statement body"
        catch ( /*IO*/ Exception e ) {
            out.printf( "--> %s%n--> return empty list%n", e );
            return Collections.emptyList();
        }
    }
...

顺便说一句,这个班级有INSTANCE名和getInstance()名成员,乍一看就像是一个单人班.但事实并非如此,因为getInstance()人中有return new WebCrawlerServiceLocator( true );人.这可能会使这个类的读者/用户感到困惑(另外没有任何Java代码).我会在代码评审中抱怨这一点,两者都是.

WebCrawlerServiceLocator测试

package com.udacity.webcrawler.service;

import static java.lang.System.out;
import static org.junit.Assert.assertFalse;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Collection;

import org.junit.jupiter.api.Test;

public final class WebCrawlerServiceLocator测试 {

    private static final int CLASS_COUNT = 3;

    @Test
    public void locateServiceWithTest_NotEmpty() {

        ServiceLocator<Class<?>> locator = WebCrawlerServiceLocator.getInstance( true );
        Collection<Class<?>> classes = locator.locateService();
        out.printf( "### locateService(): classes collection contains %d classes: %s %n",
                classes.size(), classes.toString() );
        assertFalse( "classes collection is empty", classes.isEmpty() );
        assertEquals( CLASS_COUNT, classes.size() );
    }

    @Test
    public void locateServiceWithoutTest_NotEmpty() {

        ServiceLocator<Class<?>> locator = WebCrawlerServiceLocator.getInstance( false );
        Collection<Class<?>> classes = locator.locateService();
        out.printf( "### locateService(): classes collection contains %d classes: %s %n",
                classes.size(), classes.toString() );
        assertFalse( "classes collection is empty", classes.isEmpty() );
        assertEquals( CLASS_COUNT, classes.size() );
    }

    @Test
    public void locateServiceWithTest2_NotEmpty() {

        //ServiceLocator<Class<?>> locator = WebCrawlerServiceLocator.getInstance( true ); // compile error
        // ServiceLocator has no locateService2(), hence using the following for the time being:
        WebCrawlerServiceLocator locator = (WebCrawlerServiceLocator) WebCrawlerServiceLocator.getInstance( true );

        Collection<Class<?>> classes = locator.locateService2();
        out.printf( "### locateService2(): classes collection contains %d classes: %s %n",
                classes.size(), classes.toString() );
        assertFalse( "classes collection is empty", classes.isEmpty() );
    }

    @Test
    public void locateServiceWithoutTest2_NotEmpty() {

        //ServiceLocator<Class<?>> locator = WebCrawlerServiceLocator.getInstance( false ); // compile error
        // , ServiceLocator has no locateService2(), hence using the following for the time being:
        WebCrawlerServiceLocator locator = (WebCrawlerServiceLocator) WebCrawlerServiceLocator.getInstance( false );

        Collection<Class<?>> classes = locator.locateService2();
        out.printf( "### locateService2(): classes collection contains %d classes: %s %n",
                classes.size(), classes.toString() );
        assertFalse( "classes collection is empty", classes.isEmpty() );
    }

}

MVN测试

...
[INFO] --- surefire:3.1.2:test (default-test) @ udacity-webcrawler ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.udacity.webcrawler.json.ConfigurationLoaderTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.191 s -- in com.udacity.webcrawler.json.ConfigurationLoaderTest
[INFO] Running com.udacity.webcrawler.json.CrawlResultWriterTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.021 s -- in com.udacity.webcrawler.json.CrawlResultWriterTest
[INFO] Running com.udacity.webcrawler.parser.PageParserImplTest
[ERROR] Tests run: 2, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 0.093 s <<< FAILURE! -- in com.udacity.webcrawler.parser.PageParserImplTest
...
[INFO] Running com.udacity.webcrawler.profiler.ProfilerImplTest
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.906 s -- in com.udacity.webcrawler.profiler.ProfilerImplTest
[INFO] Running com.udacity.webcrawler.service.WebCrawlerServiceLocator测试
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.724 s -- in com.udacity.webcrawler.service.WebCrawlerServiceLocator测试
[INFO] Running com.udacity.webcrawler.WordCountsTest
[ERROR] Tests run: 2, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 0.011 s <<< FAILURE! -- in com.udacity.webcrawler.WordCountsTest
...
[INFO] Running com.udacity.webcrawler.ParallelWebCrawlerTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.067 s -- in com.udacity.webcrawler.ParallelWebCrawlerTest
[INFO] Running com.udacity.webcrawler.SequentialWebCrawlerTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.013 s -- in com.udacity.webcrawler.SequentialWebCrawlerTest
[INFO] Running com.udacity.webcrawler.WebCrawlerTest
[ERROR] Tests run: 22, Failures: 14, Errors: 0, Skipped: 0, Time elapsed: 0.221 s <<< FAILURE! -- in com.udacity.webcrawler.WebCrawlerTest
...
[ERROR] Tests run: 53, Failures: 18, Errors: 0, Skipped: 0
...

因此,测试至少还在运行.他们"只是"因为断言失败而失败.

顺便说一句,我不会引入额外的/src/test/data迪尔.我会使用/src/test/resources,因为这就是它的用途-约定胜过配置.这样,您就可以在测试中使用AnyTest.class.getClassLoader().getResource("path-relative-to-resources-dir/file.name"),而不必在POM中定义<testDataDir>,更重要的是,在从IDE中运行单元测试时,可以将-DtestDataDir=...定义为VM参数.您可以使用resource filtering(实际上是字符串内插),这是一个强大的功能.

如果你给我access on GitHub美元,我可以用所有的零钱推一根 twig .

更新

最初的webCrawlerServiceLocator.locateService()款现在也能用了.我想这是因为我在POM中只为JUnit声明了以下内容:

    ...
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>5.9.3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    ...
    <dependencies>
        ...
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
        </dependency>

    </dependencies>
    ...

因此,JUnit依赖项(在某个版本中)和Maven的surefire插件(在某个版本中)之间的不兼容可能导致了最初的问题.

Java相关问答推荐

虚拟线程似乎在外部服务调用时阻止运营商线程

如果给定层次 struct 级别,如何从其预序穿越构造n元树

我的scala文件失败了Scala.g4 ANTLR语法

弹簧靴和龙目岛

JUnit—如何模拟局部变量对象方法调用

Java .类参数不通过构造函数传递

如何在带有Micronaut的YAML中使用包含特殊字符的字符串作为键

如何使用AWS CLI从S3存储桶中的所有对象中删除用户定义的元数据?

为什么我的ArrayList索引的索引总是返回-1?

相同的Java SerializedLambda为implMethodKind返回不同的结果

如何让DTO接受空字符串字段,但如果它们不为空,则应用JPA验证?

S,要对Java复制构造函数深度克隆所有属性进行单元测试,最可靠的方法是什么?

在Frege中,我如何将一个字符串安全地转换为一个可能的Int?

使用SWIG将C++自定义单元类型转换为基本Java类型

插入中的JOOQ序列,设置为VS值

AWS Java SDK v2.x中没有setObjectAcl方法

为什么mvn编译生命周期阶段不只是编译已更改的java文件?

模拟JUnit未检测到返回字符串的方法的任何声纳覆盖

如何使用外部函数从Java中获取C++ struct 的返回值&;内存API

升级版本后出现非法访问错误