Я обновил вопрос более простым примером, демонстрирующим то же поведение
Я пишу библиотеку Groovy для Дженкинса, где код написан на Groovy. Я пишу свои модульные тесты с помощью JUnit 5, но у меня возникают некоторые странные проблемы покрытия, которые я не понимаю.
Учитывая следующий класс:
package com.company.util
class StringUtil {
static String snakeToCamelCase(String text) {
return text.toLowerCase().replaceAll(/(_)([a-z])/, { it[2].toUpperCase() }).capitalize()
}
}
и выполняю следующие тесты:
package com.company.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
class StringUtilTest {
@ParameterizedTest
@MethodSource("snakeCaseToCamelCaseStrings")
void canConvertFromSnakeCaseToCamelCase(String snakeCase, String expectedCamelCase) {
String actualCamelCase = StringUtil.snakeToCamelCase(snakeCase);
assertThat(actualCamelCase).isEqualTo(expectedCamelCase);
}
private static Stream<Arguments> snakeCaseToCamelCaseStrings() {
return Stream.of(
Arguments.of("TEXT", "Text"),
Arguments.of("WITH_UNDERSCORE", "WithUnderscore"),
Arguments.of("AS_MANY_PARTS_AS_REQUIRED", "AsManyPartsAsRequired")
);
}
@Test
void throwsExceptionWhenNullStringPassed() {
Assertions.assertThrows(Throwable.class, () -> {
StringUtil.snakeToCamelCase(null);
});
}
}
Я думал, что охватил весь этот метод, но я не понимаю, почему IntelliJ отмечает закрывающие скобки как открытые, как показано на рисунке ниже
В связи с предложением Ольги Клишо, что это как-то связано с сгенерированным байт-кодом, я прикрепил здесь источник байт-кода. Я понимаю некоторый байт-код, но не понимаю, что мне нужно проверить, чтобы получить полное покрытие:
// class version 55.0 (55)
// access flags 0x21
public class com/company/util/StringUtil implements groovy/lang/GroovyObject {
// compiled from: StringUtil.groovy
// access flags 0x11
public final INNERCLASS com/company/util/StringUtil$_snakeToCamelCase_closure1 null _snakeToCamelCase_closure1
// access flags 0x100A
private static synthetic Lorg/codehaus/groovy/reflection/ClassInfo; $staticClassInfo
// access flags 0x1089
public static transient synthetic Z __$stMC
// access flags 0x1082
private transient synthetic Lgroovy/lang/MetaClass; metaClass
// access flags 0x100A
private static synthetic Lorg/codehaus/groovy/reflection/ClassInfo; $staticClassInfo$
// access flags 0x100A
private static synthetic Ljava/lang/ref/SoftReference; $callSiteArray
// access flags 0x1
public <init>()V
@Lgroovy/transform/Generated;()
L0
INVOKESTATIC com/company/util/StringUtil.$getCallSiteArray ()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
ASTORE 1
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
ALOAD 0
INVOKEVIRTUAL com/company/util/StringUtil.$getStaticMetaClass ()Lgroovy/lang/MetaClass;
ASTORE 2
ALOAD 2
ALOAD 0
SWAP
PUTFIELD com/company/util/StringUtil.metaClass : Lgroovy/lang/MetaClass;
ALOAD 2
POP
L1
LINENUMBER 7 L1
RETURN
LOCALVARIABLE this Lcom/company/util/StringUtil; L0 L1 0
MAXSTACK = 2
MAXLOCALS = 3
// access flags 0x9
public static snakeToCamelCase(Ljava/lang/String;)Ljava/lang/String;
L0
INVOKESTATIC com/company/util/StringUtil.$getCallSiteArray ()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
ASTORE 1
L1
LINENUMBER 5 L1
ALOAD 1
LDC 0
AALOAD
ALOAD 1
LDC 1
AALOAD
ALOAD 1
LDC 2
AALOAD
ALOAD 0
INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.call (Ljava/lang/Object;)Ljava/lang/Object; (itf)
LDC "(_)([a-z])"
NEW com/company/util/StringUtil$_snakeToCamelCase_closure1
DUP
LDC Lcom/company/util/StringUtil;.class
LDC Lcom/company/util/StringUtil;.class
INVOKESPECIAL com/company/util/StringUtil$_snakeToCamelCase_closure1.<init> (Ljava/lang/Object;Ljava/lang/Object;)V
INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.call (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.call (Ljava/lang/Object;)Ljava/lang/Object; (itf)
INVOKESTATIC org/codehaus/groovy/runtime/typehandling/ShortTypeHandling.castToString (Ljava/lang/Object;)Ljava/lang/String;
CHECKCAST java/lang/String
ARETURN
L2
LINENUMBER 6 L2
FRAME FULL [] [java/lang/Throwable]
NOP
ATHROW
LOCALVARIABLE text Ljava/lang/String; L0 L2 0
MAXSTACK = 8
MAXLOCALS = 2
// access flags 0x1004
protected synthetic $getStaticMetaClass()Lgroovy/lang/MetaClass;
ALOAD 0
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
LDC Lcom/company/util/StringUtil;.class
IF_ACMPEQ L0
ALOAD 0
INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass (Ljava/lang/Object;)Lgroovy/lang/MetaClass;
ARETURN
L0
FRAME SAME
GETSTATIC com/company/util/StringUtil.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo;
ASTORE 1
ALOAD 1
IFNONNULL L1
ALOAD 0
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
INVOKESTATIC org/codehaus/groovy/reflection/ClassInfo.getClassInfo (Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;
DUP
ASTORE 1
PUTSTATIC com/company/util/StringUtil.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo;
L1
FRAME APPEND [org/codehaus/groovy/reflection/ClassInfo]
ALOAD 1
INVOKEVIRTUAL org/codehaus/groovy/reflection/ClassInfo.getMetaClass ()Lgroovy/lang/MetaClass;
ARETURN
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x1001
public synthetic getMetaClass()Lgroovy/lang/MetaClass;
@Lgroovy/transform/Generated;()
@Lgroovy/transform/Internal;()
ALOAD 0
GETFIELD com/company/util/StringUtil.metaClass : Lgroovy/lang/MetaClass;
DUP
IFNULL L0
ARETURN
L0
FRAME SAME1 groovy/lang/MetaClass
POP
ALOAD 0
DUP
INVOKEVIRTUAL com/company/util/StringUtil.$getStaticMetaClass ()Lgroovy/lang/MetaClass;
PUTFIELD com/company/util/StringUtil.metaClass : Lgroovy/lang/MetaClass;
ALOAD 0
GETFIELD com/company/util/StringUtil.metaClass : Lgroovy/lang/MetaClass;
ARETURN
MAXSTACK = 2
MAXLOCALS = 1
// access flags 0x1001
public synthetic setMetaClass(Lgroovy/lang/MetaClass;)V
@Lgroovy/transform/Generated;()
@Lgroovy/transform/Internal;()
ALOAD 0
ALOAD 1
PUTFIELD com/company/util/StringUtil.metaClass : Lgroovy/lang/MetaClass;
RETURN
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x1001
public synthetic invokeMethod(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
@Lgroovy/transform/Generated;()
@Lgroovy/transform/Internal;()
ALOAD 0
INVOKEVIRTUAL com/company/util/StringUtil.getMetaClass ()Lgroovy/lang/MetaClass;
ALOAD 0
ALOAD 1
ALOAD 2
INVOKEINTERFACE groovy/lang/MetaClass.invokeMethod (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; (itf)
ARETURN
MAXSTACK = 4
MAXLOCALS = 3
// access flags 0x1001
public synthetic getProperty(Ljava/lang/String;)Ljava/lang/Object;
@Lgroovy/transform/Generated;()
@Lgroovy/transform/Internal;()
ALOAD 0
INVOKEVIRTUAL com/company/util/StringUtil.getMetaClass ()Lgroovy/lang/MetaClass;
ALOAD 0
ALOAD 1
INVOKEINTERFACE groovy/lang/MetaClass.getProperty (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object; (itf)
ARETURN
MAXSTACK = 3
MAXLOCALS = 2
// access flags 0x1001
public synthetic setProperty(Ljava/lang/String;Ljava/lang/Object;)V
@Lgroovy/transform/Generated;()
@Lgroovy/transform/Internal;()
ALOAD 0
INVOKEVIRTUAL com/company/util/StringUtil.getMetaClass ()Lgroovy/lang/MetaClass;
ALOAD 0
ALOAD 1
ALOAD 2
INVOKEINTERFACE groovy/lang/MetaClass.setProperty (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V (itf)
RETURN
MAXSTACK = 4
MAXLOCALS = 3
// access flags 0x100A
private static synthetic $createCallSiteArray_1([Ljava/lang/String;)V
ALOAD 0
LDC 0
LDC "capitalize"
AASTORE
ALOAD 0
LDC 1
LDC "replaceAll"
AASTORE
ALOAD 0
LDC 2
LDC "toLowerCase"
AASTORE
RETURN
MAXSTACK = 3
MAXLOCALS = 1
// access flags 0x100A
private static synthetic $createCallSiteArray()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray;
LDC 3
ANEWARRAY java/lang/String
ASTORE 0
ALOAD 0
INVOKESTATIC com/company/util/StringUtil.$createCallSiteArray_1 ([Ljava/lang/String;)V
NEW org/codehaus/groovy/runtime/callsite/CallSiteArray
DUP
LDC Lcom/company/util/StringUtil;.class
ALOAD 0
INVOKESPECIAL org/codehaus/groovy/runtime/callsite/CallSiteArray.<init> (Ljava/lang/Class;[Ljava/lang/String;)V
ARETURN
MAXSTACK = 4
MAXLOCALS = 1
// access flags 0x100A
private static synthetic $getCallSiteArray()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
GETSTATIC com/company/util/StringUtil.$callSiteArray : Ljava/lang/ref/SoftReference;
IFNULL L0
GETSTATIC com/company/util/StringUtil.$callSiteArray : Ljava/lang/ref/SoftReference;
INVOKEVIRTUAL java/lang/ref/SoftReference.get ()Ljava/lang/Object;
CHECKCAST org/codehaus/groovy/runtime/callsite/CallSiteArray
DUP
ASTORE 0
IFNONNULL L1
L0
FRAME SAME
INVOKESTATIC com/company/util/StringUtil.$createCallSiteArray ()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray;
ASTORE 0
NEW java/lang/ref/SoftReference
DUP
ALOAD 0
INVOKESPECIAL java/lang/ref/SoftReference.<init> (Ljava/lang/Object;)V
PUTSTATIC com/company/util/StringUtil.$callSiteArray : Ljava/lang/ref/SoftReference;
L1
FRAME APPEND [org/codehaus/groovy/runtime/callsite/CallSiteArray]
ALOAD 0
GETFIELD org/codehaus/groovy/runtime/callsite/CallSiteArray.array : [Lorg/codehaus/groovy/runtime/callsite/CallSite;
ARETURN
MAXSTACK = 3
MAXLOCALS = 1
}
IntelliJ настроен на использование IntelliJ IDEA в качестве бегунка покрытия с использованием выборки. Я попытался изменить бегунок покрытия, но не смог получить никакого покрытия.
Проект настроен и построен с использованием Gradle. Вот файл build.gradle:
plugins {
// Apply the groovy plugin to add support for Groovy
id 'groovy'
id 'java'
id 'idea'
id "org.sonarqube" version "2.7"
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
maven {
url "https://repo.jenkins-ci.org/releases/"
}
}
dependencies {
implementation 'org.codehaus.groovy:groovy-all:2.5.7'
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.0'
testImplementation 'org.assertj:assertj-core:3.14.0'
testImplementation 'org.mockito:mockito-junit-jupiter:3.2.4'
compile 'org.jenkins-ci.main:jenkins-core:2.164.1'
compile 'org.jenkins-ci.plugins.workflow:workflow-cps:2.78@jar'
compile 'com.cloudbees:groovy-cps:1.31'
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
sourceSets {
main {
groovy {
srcDirs = ['src', 'vars']
}
resources {
srcDirs = ['resources']
}
}
test {
groovy {
srcDirs = ['test']
}
}
}
У меня есть похожие результаты для большинства других файлов. Я полагаю, что это как-то связано с возвращаемым значением, поскольку конструкторы и методы void не имеют этой проблемы.