如何从单个java_test()规则在Bazel中运行所有测试?

时间:2017-09-22 13:00:46

标签: bazel

我在Bazel中添加测试,但我不想为每个测试文件编写测试规则。但是,每个测试规则都需要一个test_class - 正在运行的测试类,因此没有简单的方法可以使用单个java_test规则运行所有测试。是否有一个解决方法,我不需要指定test_class并立即运行所有测试?

4 个答案:

答案 0 :(得分:4)

在Bazel中,我们编写了一个自定义Junit Suite,它可以在带注释类的包中或包下的类路径中查找所有Junit类。您可以找到代码here。它非常简短直接,你可以将它复制到你的项目或做类似的事情。

然后您可以遵守以下规则:

java_library(
    name = "tests",
    test_only = 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 { } 

答案 1 :(得分:3)

您可以编写一个JUnit测试套件类,它将运行您的其他测试。例如,如果您有测试类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 {}

BUILD

java_test(
    name = "AllTests",
    test_class = "AllTests",
    srcs = [
        "AllTests.java",
        "Test1.java",
        "Test2.java",
    ],
)

编辑回应评论:

如果您不想在测试套件中指定测试类名称,可以通过反射执行某些操作。以下示例假设您的所有测试都在" com.foo"包和所有测试都是java_test规则的srcs:

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;
  }
}

答案 2 :(得分:1)

这是一个不需要使用Suite的解决方案。 请记住,它会为每个类单独生成覆盖率报告.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",
    ]
)

答案 3 :(得分:1)

Gerrit项目包含一个称为junit_tests的Starlark函数。它获取src列表,并生成一个AllTestsTestSuite.java文件,该文件在每个Java类中运行测试。它还会生成一个java_test目标,其中包括生成的Java文件以及所有指定的源,部门等。这是设置方法。

首先将这些行添加到您的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目标将失败,并显示“没有可运行的方法”。解决方法是将空测试添加到文件中:

/** Workaround for https://github.com/bazelbuild/bazel/issues/2539 */
@Test
public void emptyTest() {}

这对于MacOS上的Bazel 2.0.0来说对我有用。我还可以使用Bazel插件在IntelliJ 2019.2中运行测试。