java Checkstyle:使用@BeforeEach时测试中的HiddenField

6tr1vspr  于 2023-08-01  发布在  Java
关注(0)|答案(1)|浏览(143)

在Java中创建CLI测试后,我收到checkstyle错误:

[ERROR] /src/src/test/java/com/doctestbot/cli/TestCliParser.java:53:23: 'supportedOptions' hides a field. [HiddenField]
[ERROR] /src/src/test/java/com/doctestbot/cli/TestCliParser.java:61:25: 'cliParser' hides a field. [HiddenField]

字符串
我假设这是因为测试类TestCliParser的私有变量supportedOptions@BeforeEachsetup()方法中创建后,掩盖了测试方法testArgExists中的相同变量supportedOptions
我知道在类的某些函数中有一个可能被覆盖的HiddenField可能是不可取的,因此,我理解Checkstyle会抛出错误。我不太明白的是,我是否应该使用不同的方法/策略来初始化cliParsersupportedOptions对象,或者我是否应该以不同的方式将它们传递给测试函数。

代码

这是相关测试文件:

package com.doctestbot;

import com.doctestbot.cli.CliParser;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;

import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.UnrecognizedOptionException;
import org.apache.commons.cli.ParseException;

/**
 * Test class for the CliParser class.
 * This class contains test methods to verify the functionality of the
 * CliParser class.
 */
@SuppressWarnings({"PMD.AtLeastOneConstructor"})
class TestCliParser {

    /** Object that is tested.
     */
    private CliParser cliParser;
    /** A custom set of supported options are created before each test for
     * testing.
    */
    private Options supportedOptions;

    /**
    This method sets up a dummy command-line parser with test options and
    arguments before each test.
    */
    @BeforeEach
    public void setup() {
        // Create the first option with the specified properties
        final Option.Builder firstOptBuilder = Option.builder("f");
        firstOptBuilder.required(true);
        firstOptBuilder.desc("The first option");
        firstOptBuilder.longOpt("first");
        final Option firstOption = firstOptBuilder.build();

        // Create the option for logging with the specified properties
        final Option.Builder optionLogBuilder = Option.builder("l");
        optionLogBuilder.required(true);
        optionLogBuilder.desc(
            "Log the full, unparsed ChatGPT output to the request"
            );
        optionLogBuilder.longOpt("log");
        final Option optionLog = optionLogBuilder.build();

        // Create the supported options set and add the options
        final Options supportedOptions = new Options();
        supportedOptions.addOption(firstOption);
        supportedOptions.addOption(optionLog);

        final String[] emptyArguments = {};

        // Create and initialize the CliParser instance with dummy arguments
        @SuppressWarnings({"PMD.UnusedLocalVariable"})
        final CliParser cliParser = new CliParser(emptyArguments);
    }

    /**
     * Test method to verify the CLI verification function works if it receives
     * expected arguments.
     */
    @Test
    void testArgExists() {
        // Dummy command-line arguments for testing
        final String[] dummyArguments = {"-f", "theValueOfA", "--log"};

        // Verify no error is thrown upon valid arguments.
         assertDoesNotThrow(() -> cliParser.verifyArgsAreSupported(
            supportedOptions, dummyArguments));
    }

    /**
     * Test method to verify the verifyArgsAreSupported method throws an error
     * if an unexpected argument is passed.
     */
    @Test
    @SuppressWarnings({"PMD.JUnitTestContainsTooManyAsserts",
    "PMD.LawOfDemeter"})
    void testUnknownArgThrowsError() {

        // Dummy command-line arguments for testing
        final String[] dummyArguments = {"-f", "theValueOfA", "--log",
        "--someUnsupportedArg"};

        // Verify that the provided arguments match the supported options

        // Assert that the UnrecognizedOptionException is thrown and its cause
        // is ParseException.
         final Exception exception = assertThrows(
            UnrecognizedOptionException.class, () -> {
            cliParser.verifyArgsAreSupported(supportedOptions, dummyArguments);
        });

        // Assert the cause of the UnrecognizedOptionException
        final Throwable cause = exception.getCause();
        assertTrue(cause instanceof ParseException,
        "The error was thrown due to a ParseException.");
    }
}

提问

如何在@BeforeEachsetup()函数中初始化cliParsersupportedOptions的同时解决HiddenField错误?

回复评论编辑

我在评论中指出的错误对我来说是有意义的。当我改变:

final Options supportedOptions = new Options();


致:

supportedOptions = new Options();


Checkstyle抛出错误:

checkstyle...............................................................Failed
- hook id: checkstyle
- exit code: 254

Starting audit...
com.puppycrawl.tools.checkstyle.api.CheckstyleException: Exception was thrown while processing src/test/java/com/doctestbot/cli/TestCliParser.java
    at com.puppycrawl.tools.checkstyle.Checker.processFiles(Checker.java:306)
    at com.puppycrawl.tools.checkstyle.Checker.process(Checker.java:223)
    at com.puppycrawl.tools.checkstyle.Main.runCheckstyle(Main.java:415)
    at com.puppycrawl.tools.checkstyle.Main.runCli(Main.java:338)
    at com.puppycrawl.tools.checkstyle.Main.execute(Main.java:195)
    at com.puppycrawl.tools.checkstyle.Main.main(Main.java:130)
Caused by: com.puppycrawl.tools.checkstyle.api.CheckstyleException: IllegalStateException occurred while parsing file /src/src/test/java/com/doctestbot/cli/TestCliParser.java.
    at com.puppycrawl.tools.checkstyle.JavaParser.parse(JavaParser.java:105)
    at com.puppycrawl.tools.checkstyle.TreeWalker.processFiltered(TreeWalker.java:152)
    at com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck.process(AbstractFileSetCheck.java:98)
    at com.puppycrawl.tools.checkstyle.Checker.processFile(Checker.java:334)
    at com.puppycrawl.tools.checkstyle.Checker.processFiles(Checker.java:293)
    ... 5 more
Caused by: java.lang.IllegalStateException: 37:61: mismatched input '(' expecting ';'
    at com.puppycrawl.tools.checkstyle.JavaParser$CheckstyleErrorListener.syntaxError(JavaParser.java:255)
    at org.antlr.v4.runtime.ProxyErrorListener.syntaxError(ProxyErrorListener.java:41)
    at org.antlr.v4.runtime.Parser.notifyErrorListeners(Parser.java:544)
    at org.antlr.v4.runtime.DefaultErrorStrategy.reportInputMismatch(DefaultErrorStrategy.java:327)
    at org.antlr.v4.runtime.DefaultErrorStrategy.reportError(DefaultErrorStrategy.java:139)
    at com.puppycrawl.tools.checkstyle.CheckstyleParserErrorStrategy.recoverInline(CheckstyleParserErrorStrategy.java:38)
    at org.antlr.v4.runtime.Parser.match(Parser.java:208)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.blockStatement(JavaLanguageParser.java:6189)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.block(JavaLanguageParser.java:6100)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.methodBody(JavaLanguageParser.java:2939)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.methodDeclaration(JavaLanguageParser.java:2897)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.memberDeclaration(JavaLanguageParser.java:2744)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.classBodyDeclaration(JavaLanguageParser.java:2670)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.classBody(JavaLanguageParser.java:2476)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.classDeclaration(JavaLanguageParser.java:1095)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.types(JavaLanguageParser.java:752)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.typeDeclaration(JavaLanguageParser.java:666)
    at com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser.compilationUnit(JavaLanguageParser.java:413)
    at com.puppycrawl.tools.checkstyle.JavaParser.parse(JavaParser.java:99)
    ... 9 more
Caused by: org.antlr.v4.runtime.InputMismatchException
    ... 23 more
Checkstyle ends with 1 errors.

rxztt3cl

rxztt3cl1#

通过实施评论中给出的建议找到了解决方案:

package com.doctestbot.cli;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.ParseException;

/**
 * The CLI parser for the documentation and test writing bot.
 * This class provides functionality to parse and verify command-line arguments
 * for the bot.
 */
public class CliParser {

    /**
    * Declare the CLI argument option objects.
    */
    private final Options cliOptions;
    /** Create a proper logger for error messages. */
    private static final Logger LOGGER = LoggerFactory.getLogger(
        CliParser.class
        );

    /**
     * Constructs a new CliParser with the given command-line arguments.
     *
     * @param arguments The command-line arguments provided to the bot.
     */
    public CliParser(final String... arguments) {
        this.cliOptions = buildSupportedArgs();
        verifyArgsAreSupported(this.cliOptions, arguments);
    }

    /**
    * Adds logging options to the supported command-line options.
    *
    * @param supportedOptions The Options object to which the logging option
    * will be added.
    */
    @SuppressWarnings({"PMD.LawOfDemeter", "PMD.AvoidDuplicateLiterals"})
    private static void addLogOptions(final Options supportedOptions) {
        // Logging option
        final Option.Builder logBldr = Option.builder("l");
        logBldr.required(false);
        logBldr.desc(
            "Log the full, unparsed ChatGPT output to the request.");
        logBldr.longOpt("log");
        final Option logArg = logBldr.build();
        supportedOptions.addOption(logArg);
    }

    /**
    * Adds ChatGPT output verification CLI options to supportedOptions.
    *
    * @param supportedOptions The Options object to which the output
    * verification options will be added.
    */
    @SuppressWarnings({"PMD.LawOfDemeter", "PMD.AvoidDuplicateLiterals"})
    private static void addOutputVerifOptions(final Options supportedOptions) {
        final Option.Builder preCommitBldr = Option.builder("pc");
        preCommitBldr.desc(
            "Perform pre-commit checks before committing changes.");
        preCommitBldr.longOpt("pre-commit-checks");
        final Option preCommitArg = preCommitBldr.build();
        supportedOptions.addOption(preCommitArg);

        final Option.Builder compilabilityBldr = Option.builder("cc");
        compilabilityBldr.desc(
            "Perform compilability check.");
        compilabilityBldr.longOpt("compilability");
        final Option compilabilityArg = compilabilityBldr.build();
        supportedOptions.addOption(compilabilityArg);

        final Option.Builder runnabililtyBldr = Option.builder("rc");
        runnabililtyBldr.desc(
            "Enforce runnability compliance for generated code.");
        runnabililtyBldr.longOpt("runnability-compliance");
        final Option runnabililtyArg = runnabililtyBldr.build();
        supportedOptions.addOption(runnabililtyArg);

        final Option.Builder testsBldr = Option.builder("tc");
        testsBldr.desc(
            "Enforce the tests pass for generated code.");
        testsBldr.longOpt("test-compliance");
        final Option testsArg = testsBldr.build();
        supportedOptions.addOption(testsArg);

        final Option.Builder retriesBldr = Option.builder("n");
        retriesBldr.desc(
            "Specify the number of retries for certain actions (default: 1)."
            );
        retriesBldr.longOpt("retries");
        retriesBldr.hasArg();
        final Option retriesArg = retriesBldr.build();
        supportedOptions.addOption(retriesArg);
    }

    /**
    * Adds options for user to specify the path to source and test files of the
    * target repository.
    *
    * @param supportedOptions The Options object to which the target paths
    * options will be added.
    */
    @SuppressWarnings({"PMD.LawOfDemeter", "PMD.AvoidDuplicateLiterals"})
    private static void addTargetPathsOptions(final Options supportedOptions) {
        final Option.Builder srcBldr = Option.builder("sd");
        srcBldr.desc(
            "The relative directory from the root dir to the dir with the "
            + "project code.");
        srcBldr.longOpt("source-dir");
        srcBldr.hasArg();
        final Option srcArg = srcBldr.build();
        supportedOptions.addOption(srcArg);

        final Option.Builder testBldr = Option.builder("td");
        testBldr.desc(
            "The relative directory from the root dir to the dir with the "
            + "tests.");
        testBldr.longOpt("test-dir");
        testBldr.hasArg();
        final Option testArg = testBldr.build();
        supportedOptions.addOption(testArg);
    }

    /**
     * Adds options to specify the context to feed to ChatGPT for the target
     * repository.
     *
     * @param supportedOptions The Options object to which the context options
     * will be added.
     */
    @SuppressWarnings({"PMD.LawOfDemeter", "PMD.AvoidDuplicateLiterals",
    "PMD.LongVariable"})
    private static void addGptContextOptions(final Options supportedOptions) {
        // What context to feed to ChatGPT (once) supportedOptions
        final Option.Builder shareSrcBldr = Option.builder("ssd");
        shareSrcBldr.desc(
            "Share the source dir files of the specified language with"
            + " ChatGPT.");
        shareSrcBldr.longOpt("share-source-dir");
        final Option shareSrcArg = shareSrcBldr.build();
        supportedOptions.addOption(shareSrcArg);

        final Option.Builder shareTestBldr = Option.builder("std");
        shareTestBldr.desc(
            "Share the test dir files of the specified language with "
            + "ChatGPT.");
        shareTestBldr.longOpt("share-test-dir");
        final Option shareTestArg = shareTestBldr.build();
        supportedOptions.addOption(shareTestArg);

        final Option.Builder shareSrcStructBldr = Option.builder("ssds");
        shareSrcStructBldr.desc(
            "Share the source dir files of the specified language with "
            + "ChatGPT.");
        shareSrcStructBldr.longOpt("share-source-dir-structure");
        final Option shareSrcStructArg = shareSrcStructBldr.build();
        supportedOptions.addOption(shareSrcStructArg);

        final Option.Builder shareTestStructBldr = Option.builder(
            "stds");
        shareTestStructBldr.desc(
            "Share the test dir files of the specified language with ChatGPT."
            );
        shareTestStructBldr.longOpt("share-test-dir-structure");
        final Option shareTestStructArg = shareTestStructBldr.build();
        supportedOptions.addOption(shareTestStructArg);
    }

   /**
    * Adds options for specifying the which files will be processed.
    *
    * @param supportedOptions The Options object to which the processed code
    * type options will be added.
    */
    @SuppressWarnings({"PMD.LawOfDemeter", "PMD.AvoidDuplicateLiterals"})
    private static void addTargetFileOptions(final Options supportedOptions) {
        final Option.Builder languageBldr = Option.builder("l");
        languageBldr.desc(
            "Specify the programming language of the target project.");
        languageBldr.longOpt("language");
        languageBldr.hasArg();
        final Option languageArg = languageBldr.build();
        supportedOptions.addOption(languageArg);

        final Option.Builder allFilesBldr = Option.builder("a");
        allFilesBldr.desc(
            "Perform the specified action on all source files in the"
            + " repository.");
        allFilesBldr.longOpt("all-files");
        final Option allFilesArg = allFilesBldr.build();
        supportedOptions.addOption(allFilesArg);

        final Option.Builder fileSubstrBldr = Option.builder("fs");
        fileSubstrBldr.desc(
            "Specify the substring that filenames should contain.");
        fileSubstrBldr.longOpt("file-substring");
        fileSubstrBldr.hasArg();
        final Option fileSubstrArg = fileSubstrBldr.build();
        supportedOptions.addOption(fileSubstrArg);

        final Option.Builder funcBldr = Option.builder("ms");
        funcBldr.desc(
            "Specify the name of the method to run the bot on.");
        funcBldr.longOpt("method");
        funcBldr.hasArg();
        final Option funcArg = funcBldr.build();
        supportedOptions.addOption(funcArg);

        final Option.Builder docstrBldr = Option.builder("ds");
        docstrBldr.desc(
            "Run on docstring of file.");
        docstrBldr.longOpt("docstring");
        final Option docstrArg = docstrBldr.build();
        supportedOptions.addOption(docstrArg);
    }

    /**
    * Adds options for specifying the type of processed code and the actions
    * to perform.
    *
    * @param supportedOptions The Options object to which the processed code
    * type options will be added.
    */
    @SuppressWarnings({"PMD.LawOfDemeter", "PMD.AvoidDuplicateLiterals"})
    private static void addCodeTypeOptions(final Options supportedOptions) {

        final Option.Builder docuBldr = Option.builder("d");
        docuBldr.desc(
            "Perform the documentation action for identified functions.");
        docuBldr.longOpt("documentation");
        final Option docuArg = docuBldr.build();
        supportedOptions.addOption(docuArg);

        final Option.Builder testGenBldr = Option.builder("t");
        testGenBldr.desc(
            "Perform the test generation action for identified functions.");
        testGenBldr.longOpt("test-generation");
        final Option testGenArg = testGenBldr.build();
        supportedOptions.addOption(testGenArg);

        final Option.Builder commentsBldr = Option.builder("c");
        commentsBldr.desc(
            "Improve comments in the function.");
        commentsBldr.longOpt("comments");
        final Option commentsArg = commentsBldr.build();
        supportedOptions.addOption(commentsArg);

        final Option.Builder overrideBldr = Option.builder("o");
        overrideBldr.desc(
            "Override pre-existing (DOCUMENTATION, COMMENTS, FUNCTIONS,"
            + " TESTS).");
        overrideBldr.longOpt("override");
        final Option overrideArg = overrideBldr.build();
        supportedOptions.addOption(overrideArg);
    }

    /**
     * Builds and returns the supported command-line options.
     *
     * @return The Options object containing the supported command-line options.
     */
    @SuppressWarnings({"PMD.LawOfDemeter",
    "PMD.NcssCount", "PMD.LongVariable"})
    private Options buildSupportedArgs() {
        final Options supportedOptions = new Options();
        addLogOptions(supportedOptions);
        addOutputVerifOptions(supportedOptions);
        addTargetPathsOptions(supportedOptions);
        addGptContextOptions(supportedOptions);
        addTargetFileOptions(supportedOptions);
        addCodeTypeOptions(supportedOptions);
        return supportedOptions;
    }

    /**
     * Prints the usage instructions for running the bot from the command line.
     * It provides information about the available command-line options and
     * their usage.
     */
    private void printUsage() {
        final HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("java -jar YourJarName.jar", cliOptions);
    }

    /**
     * Verifies whether the provided command-line arguments are supported by
     * the bot. Prints usage for invalid commands.
     *
     * @param options The Options object containing the supported command-line
     *  options.
     * @param args    The command-line arguments provided to the bot.
     */
    public final void verifyArgsAreSupported(
        final Options options,
        final String... args
    ) {
        final CommandLineParser parser = new DefaultParser();
        try {
            parser.parse(options, args);
        } catch (ParseException e) {
            if (LOGGER.isErrorEnabled()) { // Check if ERROR level is enabled
                LOGGER.error(
                    "Error parsing command-line arguments: {}", e.getMessage()
                );
            }
            printUsage();
            throw new CommandLineParseException(
                "Error parsing command-line arguments", e);
        }
    }
}

字符串
问题最后提到的另一个错误,通过将两个私有字段的初始化移动到单独的setter和getter方法中,并从@BeforeEachsetup()方法调用这些方法来解决。* 我还不知道为什么这是必要的/为什么错误被抛出。*

注意事项

不需要在测试中创建测试类的对象/构造函数来调用带有this.supportedOptions的私有字段。相反,我可以将它们作为测试的全局变量。

相关问题