After creating a CLI test in Java, I receive the checkstyle Error:
[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]
I assume this is because the private variable supportedOptions of the test class TestCliParser overshadows the same variable supportedOptions in the test method testArgExists after it is created in the @BeforeEach setup() method.
I understand that having a HiddenField that may be overwritten in some function of the class may be undesirable, hence, I understand Checkstyle throws the error. What I do not quite understand is whether I should be using a different approach/strategy to initialise the cliParser and supportedOptions objects, or whether should I pass them differently to the test functions.
Code
This is the relevant test file:
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.");
}
}
Question
How can I resolve the HiddenField error whilst still initialising the cliParser and supportedOptions in the @BeforeEach setup() function?
Edit in response to comments
The error I made pointed out in the comments makes sense to me. However, when I change:
final Options supportedOptions = new Options();
to:
supportedOptions = new Options();
Checkstyle throws error:
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.
A solution was found by implementing the suggestion given in the comment:
The additional error, mentioned at the end of the question, was resolved by moving the initialisation of the two private fields into separate setter and getter methods, and calling those methods from the
@BeforeEachsetup()method. I do not yet know why this was necessary/why the error was thrown.Note
It was not necessary to create an object/constructor of the test class inside the test to call the private fields with
this.supportedOptions. Instead I could just keep them as globals of the test.