[英]Java unit test run successfully on Intellij but fail on maven
I am facing an issue that the unit test cases pass on IntelliJ but fail on mvn test
command.我面临一个问题,即单元测试用例在 IntelliJ 上通过但在mvn test
命令上失败。 I think the problem is about java 17 but I'm not sure.我认为问题出在 java 17 上,但我不确定。 Src code can be found here源代码可以在这里找到
Maven version (with jdk 17): 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: 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>
Here is the code:这是代码:
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;
}
}
}
Exception was thrown at the line private final Profiler profiler = new ProfilerImpl(clock, true);
在行private final Profiler profiler = new ProfilerImpl(clock, true);
抛出异常and I don't know why.我不知道为什么。 Please check the below code:请检查以下代码:
/**
* 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());
}
}
Stack trace:堆栈跟踪:
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
Please help请帮忙
After the actions in the comments to the question I:在对问题的评论中的操作之后,我:
.peek(System.out::println)
prior to (line 52) filter(classInfo ->...)
in WebCrawlerServiceLocator
,在WebCrawlerServiceLocator
的(第 52 行) filter(classInfo ->...)
之前添加.peek(System.out::println)
,mvn test -Dmaven.surefire.debug > mvn-test.out
and运行mvn test -Dmaven.surefire.debug > mvn-test.out
和target.classes.com.udacity.webcrawler.json.ConfigurationLoader
followed by the error mentioned in the question and its comments:在第 5617 行,其中有target.classes.com.udacity.webcrawler.json.ConfigurationLoader
,然后是问题中提到的错误及其注释:...
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)
...
So, apparently ImmutableSet<ClassInfo> com.google.common.reflect.ClassPath.getAllClasses()
takes Maven's ${project.basedir}
as class path entry.因此,显然ImmutableSet<ClassInfo> com.google.common.reflect.ClassPath.getAllClasses()
将 Maven 的${project.basedir}
作为 class 路径条目。 The problem is:问题是:
target/classes/
from packageName
in map(info -> {... })
since ClassPath.ClassInfo
has no setters你不能简单地从map(info -> {... })
中的packageName
中删除target/classes/
因为ClassPath.ClassInfo
没有设置器ClassInfo(File file, String resourceName, ClassLoader loader)
but with default (package) access only.它有一个构造函数ClassInfo(File file, String resourceName, ClassLoader loader)
但只有默认(包)访问。 Anyway, ClassPath
's javadoc mentions at the very beginning:无论如何, ClassPath
的javadoc在一开始就提到了:
Prefer ClassGraph over
ClassPath
比ClassPath
更喜欢ClassGraph
Out of curiosity I implemented the usage of ClassGraph :出于好奇,我实现了ClassGraph的用法:
...
<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();
}
}
...
BTW, with the INSTANCE
and getInstance()
members this class looks like intended to be a Singleton at first sight.顺便说一句,对于INSTANCE
和getInstance()
成员,这个 class 乍一看像是 Singleton。 But it isn't.但事实并非如此。 Since there's return new WebCrawlerServiceLocator( true );
因为有return new WebCrawlerServiceLocator( true );
in getInstance()
.在getInstance()
中。 That might confuse readers/users of this class (without any Javadoc in addition).这可能会使这个 class 的读者/用户感到困惑(另外没有任何 Javadoc)。 I'd complain about that in a code review, about both.我会在代码审查中抱怨这两者。
WebCrawlerServiceLocatorTest
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 WebCrawlerServiceLocatorTest {
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 test
...
[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.WebCrawlerServiceLocatorTest
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.724 s -- in com.udacity.webcrawler.service.WebCrawlerServiceLocatorTest
[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
...
So, the tests run, at least.因此,至少测试运行了。 They "just" fail due to failing assertions.由于断言失败,他们“只是”失败了。
And, BTW, I'd not introduce an extra /src/test/data
dir.而且,顺便说一句,我不会引入额外的/src/test/data
目录。 I'd use /src/test/resources
because that's what it is for – Convention over Configuration.我会使用/src/test/resources
因为这就是它的用途——约定优于配置。 Such you can use AnyTest.class.getClassLoader().getResource("path-relative-to-resources-dir/file.name")
in your tests and avoid having to define <testDataDir>
in your POM and, more important, to define -DtestDataDir=...
as VM argument when running unit tests from within your IDE. And you can use resource filtering (which is string interpolation, in fact), a powerful feature.这样您可以在测试中使用AnyTest.class.getClassLoader().getResource("path-relative-to-resources-dir/file.name")
并避免在 POM 中定义<testDataDir>
,更重要的是,在 IDE 中运行单元测试时将-DtestDataDir=...
定义为 VM 参数。并且您可以使用资源过滤(实际上是字符串插值),这是一项强大的功能。
If you grant me access on GitHub I can push a branch with all the changes.如果您授予我访问 GitHub 的权限,我可以推送包含所有更改的分支。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.