IntelliJ, Groovy, JUnit и тестовое покрытие - PullRequest
1 голос
/ 28 января 2020

Я обновил вопрос более простым примером, демонстрирующим то же поведение

Я пишу библиотеку 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 отмечает закрывающие скобки как открытые, как показано на рисунке ниже

enter image description here

В связи с предложением Ольги Клишо, что это как-то связано с сгенерированным байт-кодом, я прикрепил здесь источник байт-кода. Я понимаю некоторый байт-код, но не понимаю, что мне нужно проверить, чтобы получить полное покрытие:

// 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 не имеют этой проблемы.

...