在 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]
我认为这是因为在
supportedOptions
TestCliParser
方法中创建后,测试类 supportedOptions
的私有变量 testArgExists
掩盖了测试方法 @BeforeEach
中的同一个变量 setup()
。
我知道在类的某些函数中拥有一个可能被覆盖的 HiddenField 可能是不可取的,因此,我知道 Checkstyle 会抛出错误。我不太明白的是我是否应该使用不同的方法/策略来初始化
cliParser
和 supportedOptions
对象,或者我是否应该将它们以不同的方式传递给测试函数。
这是相关的测试文件:
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.");
}
}
如何解决 HiddenField 错误,同时仍在
cliParser
supportedOptions
函数中初始化 @BeforeEach
和 setup()
?
我在评论中指出的错误对我来说是有道理的。然而,当我改变时:
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.
通过实施评论中给出的建议找到了解决方案:
package com.doctestbot;
import com.doctestbot.cli.CliParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertTrue;
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();
setSupportedOptions(firstOption, optionLog);
setCliParser();
}
/**
* 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",
"--someUnexpectedArgument"};
// 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.");
}
/**
* Getter method for the supportedOptions.
*
* @return The supported options.
*/
public CliParser getCliParser() {
return cliParser;
}
/**
* Sets the supported options for testing.
*/
@SuppressWarnings({"PMD.DetachedTestCase"})
public void setCliParser() {
final String[] emptyArguments = {};
// Create and initialize the CliParser instance with dummy arguments
// @SuppressWarnings({"PMD.UnusedLocalVariable"})
cliParser = new CliParser(emptyArguments);
}
/**
* Getter method for the supportedOptions.
*
* @return The supported options.
*/
public Options getSupportedOptions() {
return supportedOptions;
}
/**
* Sets the supported options for testing.
*
* @param firstOption The first option to add.
* @param optionLog The option for logging.
*/
public void setSupportedOptions(final Option firstOption,
final Option optionLog) {
// Create the supported options set and add the options
supportedOptions = new Options();
supportedOptions.addOption(firstOption);
supportedOptions.addOption(optionLog);
}
}
通过将两个私有字段的初始化移动到单独的 setter 和 getter 方法中,并从
@BeforeEach
setup()
方法调用这些方法,解决了额外的错误。没有必要在测试中创建测试类的对象/构造函数来使用 this.supportedOptions
调用私有字段。相反,我可以将它们保留为测试的全局变量。