如何从单个 java_test() 规则 运行 Bazel 中的所有测试?
How to run all tests in Bazel from a single java_test() rule?
我正在 Bazel 中添加测试,但我不想为每个测试文件编写测试规则。但是,每个测试规则都需要一个 test_class - 测试 class 即 运行,因此没有简单的方法来 运行 所有测试只用一个 [=15] =] 规则。是否可以解决我不需要指定 test_class 而只需要一次 运行 所有测试的问题?
您可以编写一个 JUnit 测试套件 class,它将 运行 您的其他测试。例如,如果你有测试 classes Test1.java 和 Test2.java,你可以这样做:
AllTests.java
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
Test1.class,
Test2.class
})
public class AllTests {}
建造
java_test(
name = "AllTests",
test_class = "AllTests",
srcs = [
"AllTests.java",
"Test1.java",
"Test2.java",
],
)
编辑以回应评论:
如果您不想在测试套件中指定测试 class 名称,您可以通过反射来做一些事情。以下示例假设您所有的测试都在 "com.foo" 包中,并且所有测试都是 java_test 规则的 src:
package com.foo;
import java.io.File;
import java.io.IOException;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import junit.framework.JUnit4TestAdapter;
import junit.framework.TestSuite;
import org.junit.runner.RunWith;
@RunWith(org.junit.runners.AllTests.class)
public class AllTests {
public static TestSuite suite() throws IOException {
TestSuite suite = new TestSuite();
URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
// The first entry on the classpath contains the srcs from java_test
findClassesInJar(new File(classLoader.getURLs()[0].getPath()))
.stream()
.map(c -> {
try {
return Class.forName(c);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.filter(clazz -> !clazz.equals(AllTests.class))
.map(JUnit4TestAdapter::new)
.forEach(suite::addTest);
return suite;
}
private static Set<String> findClassesInJar(File jarFile) {
Set<String> classNames = new TreeSet<>();
try {
try (ZipFile zipFile = new ZipFile(jarFile)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
String entryName = entries.nextElement().getName();
if (entryName.startsWith("com/foo") && entryName.endsWith(".class")) {
int classNameEnd = entryName.length() - ".class".length();
classNames.add(entryName.substring(0, classNameEnd).replace('/', '.'));
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return classNames;
}
}
在Bazel, we wrote a custom Junit Suite that finds all the Junit classes on the classpath in or under the package of the annotated class. You can find the code here。它非常简短直接,您可以将其复制到您的项目中或做类似的事情。
然后您可以这样设置规则:
java_library(
name = "tests",
testonly = 1,
srcs = glob(["*.java"])
)
java_test(
name = "MyTests",
test_class = "MyTests",
runtime_deps = [":tests"],
)
和 MyTests.java 文件应如下所示:
import package.ClasspathSuite;
import org.junit.runner.RunWith;
@RunWith(ClasspathSuite.class)
public class MyTests { }
这是一个不需要使用 Suite 的解决方案。
请记住,它将为每个 class.
单独生成覆盖率报告 .dat 文件
.bzl 宏来处理所有测试:
def run_tests(name, srcs, package, deps):
for src in srcs:
src_name = src[:-5]
native.java_test(name=src_name, test_class=package + "." + src_name, srcs=srcs, deps=deps, size="small")
从测试文件位置调用该宏:
run_tests(
name = "test",
srcs = glob(["*Test.java"]),
package = "pkg",
deps = [
":src_lib",
]
)
Gerrit 项目包含一个名为 junit_tests
的 Starlark 函数。它获取一个 src 列表并生成一个 AllTestsTestSuite.java
文件,该文件 运行s 在每个 Java class 中进行测试。它还会生成一个 java_test
目标,其中包括生成的 java 文件和所有指定的源、deps 等。下面是如何设置它。
首先将这些行添加到您的 WORKSPACE
文件中:
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Re-usable building blocks for Bazel build tool
# https://gerrit.googlesource.com/bazlets/
# https://gerrit.googlesource.com/bazlets/+/968b97fa03a9d2afd760f2e8ede3d5643da390d2
git_repository(
name = "com_googlesource_gerrit_bazlets",
remote = "https://gerrit.googlesource.com/bazlets",
commit = "968b97fa03a9d2afd760f2e8ede3d5643da390d2",
)
# We cannot use the tar.gz provided over HTTP because it contains timestamps and each download has a
# different hash.
#http_archive(
# name = "com_googlesource_gerrit_bazlets",
# sha256 = "...",
# urls = [
# "https://gerrit.googlesource.com/bazlets/+archive/968b97fa03a9d2afd760f2e8ede3d5643da390d2.tar.gz",
# ],
#)
# This provides these useful imports:
# load("@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl", "maven_jar")
# load("@com_googlesource_gerrit_bazlets//tools:junit.bzl", "junit_tests")
现在将此添加到您的 BUILD
文件中:
load("@com_googlesource_gerrit_bazlets//tools:junit.bzl", "junit_tests")
junit_tests(
name = "AllTests",
srcs = glob(["*.java"]),
deps = [
"//java/com/company/a_package",
"@maven//:junit_junit",
"@maven//:org_hamcrest_hamcrest",
],
)
如果您的 BUILD
文件位于 $WORKSPACE_ROOT/javatests/com/company/a_package/BUILD
,那么您可以 运行 这些特定测试:
bazel test //javatests/com/company/a_package:AllTests
您可以 运行 所有 java 测试如下:
bazel test //javatests/...
如果您的目录包含 .java
没有测试的文件,AllTests 目标将失败并返回 "No runnable methods"。解决方法是向文件添加一个空测试:
/** Workaround for https://github.com/bazelbuild/bazel/issues/2539 */
@Test
public void emptyTest() {}
这适用于我在 MacOS 上使用 Bazel 2.0.0。我还可以 运行 使用 Bazel 插件在 IntelliJ 2019.2 中进行测试。
我正在 Bazel 中添加测试,但我不想为每个测试文件编写测试规则。但是,每个测试规则都需要一个 test_class - 测试 class 即 运行,因此没有简单的方法来 运行 所有测试只用一个 [=15] =] 规则。是否可以解决我不需要指定 test_class 而只需要一次 运行 所有测试的问题?
您可以编写一个 JUnit 测试套件 class,它将 运行 您的其他测试。例如,如果你有测试 classes Test1.java 和 Test2.java,你可以这样做:
AllTests.java
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
Test1.class,
Test2.class
})
public class AllTests {}
建造
java_test(
name = "AllTests",
test_class = "AllTests",
srcs = [
"AllTests.java",
"Test1.java",
"Test2.java",
],
)
编辑以回应评论:
如果您不想在测试套件中指定测试 class 名称,您可以通过反射来做一些事情。以下示例假设您所有的测试都在 "com.foo" 包中,并且所有测试都是 java_test 规则的 src:
package com.foo;
import java.io.File;
import java.io.IOException;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import junit.framework.JUnit4TestAdapter;
import junit.framework.TestSuite;
import org.junit.runner.RunWith;
@RunWith(org.junit.runners.AllTests.class)
public class AllTests {
public static TestSuite suite() throws IOException {
TestSuite suite = new TestSuite();
URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
// The first entry on the classpath contains the srcs from java_test
findClassesInJar(new File(classLoader.getURLs()[0].getPath()))
.stream()
.map(c -> {
try {
return Class.forName(c);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.filter(clazz -> !clazz.equals(AllTests.class))
.map(JUnit4TestAdapter::new)
.forEach(suite::addTest);
return suite;
}
private static Set<String> findClassesInJar(File jarFile) {
Set<String> classNames = new TreeSet<>();
try {
try (ZipFile zipFile = new ZipFile(jarFile)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
String entryName = entries.nextElement().getName();
if (entryName.startsWith("com/foo") && entryName.endsWith(".class")) {
int classNameEnd = entryName.length() - ".class".length();
classNames.add(entryName.substring(0, classNameEnd).replace('/', '.'));
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return classNames;
}
}
在Bazel, we wrote a custom Junit Suite that finds all the Junit classes on the classpath in or under the package of the annotated class. You can find the code here。它非常简短直接,您可以将其复制到您的项目中或做类似的事情。
然后您可以这样设置规则:
java_library(
name = "tests",
testonly = 1,
srcs = glob(["*.java"])
)
java_test(
name = "MyTests",
test_class = "MyTests",
runtime_deps = [":tests"],
)
和 MyTests.java 文件应如下所示:
import package.ClasspathSuite;
import org.junit.runner.RunWith;
@RunWith(ClasspathSuite.class)
public class MyTests { }
这是一个不需要使用 Suite 的解决方案。 请记住,它将为每个 class.
单独生成覆盖率报告 .dat 文件.bzl 宏来处理所有测试:
def run_tests(name, srcs, package, deps):
for src in srcs:
src_name = src[:-5]
native.java_test(name=src_name, test_class=package + "." + src_name, srcs=srcs, deps=deps, size="small")
从测试文件位置调用该宏:
run_tests(
name = "test",
srcs = glob(["*Test.java"]),
package = "pkg",
deps = [
":src_lib",
]
)
Gerrit 项目包含一个名为 junit_tests
的 Starlark 函数。它获取一个 src 列表并生成一个 AllTestsTestSuite.java
文件,该文件 运行s 在每个 Java class 中进行测试。它还会生成一个 java_test
目标,其中包括生成的 java 文件和所有指定的源、deps 等。下面是如何设置它。
首先将这些行添加到您的 WORKSPACE
文件中:
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Re-usable building blocks for Bazel build tool
# https://gerrit.googlesource.com/bazlets/
# https://gerrit.googlesource.com/bazlets/+/968b97fa03a9d2afd760f2e8ede3d5643da390d2
git_repository(
name = "com_googlesource_gerrit_bazlets",
remote = "https://gerrit.googlesource.com/bazlets",
commit = "968b97fa03a9d2afd760f2e8ede3d5643da390d2",
)
# We cannot use the tar.gz provided over HTTP because it contains timestamps and each download has a
# different hash.
#http_archive(
# name = "com_googlesource_gerrit_bazlets",
# sha256 = "...",
# urls = [
# "https://gerrit.googlesource.com/bazlets/+archive/968b97fa03a9d2afd760f2e8ede3d5643da390d2.tar.gz",
# ],
#)
# This provides these useful imports:
# load("@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl", "maven_jar")
# load("@com_googlesource_gerrit_bazlets//tools:junit.bzl", "junit_tests")
现在将此添加到您的 BUILD
文件中:
load("@com_googlesource_gerrit_bazlets//tools:junit.bzl", "junit_tests")
junit_tests(
name = "AllTests",
srcs = glob(["*.java"]),
deps = [
"//java/com/company/a_package",
"@maven//:junit_junit",
"@maven//:org_hamcrest_hamcrest",
],
)
如果您的 BUILD
文件位于 $WORKSPACE_ROOT/javatests/com/company/a_package/BUILD
,那么您可以 运行 这些特定测试:
bazel test //javatests/com/company/a_package:AllTests
您可以 运行 所有 java 测试如下:
bazel test //javatests/...
如果您的目录包含 .java
没有测试的文件,AllTests 目标将失败并返回 "No runnable methods"。解决方法是向文件添加一个空测试:
/** Workaround for https://github.com/bazelbuild/bazel/issues/2539 */
@Test
public void emptyTest() {}
这适用于我在 MacOS 上使用 Bazel 2.0.0。我还可以 运行 使用 Bazel 插件在 IntelliJ 2019.2 中进行测试。