Skip to content

Commit 4181b9c

Browse files
committed
Make quote character in @CsvFileSource configurable
Prior to this commit, the quote character for quoted strings in @CsvFileSource was hard coded to a double quote (") and could not be changed. Commit f1cbfbe introduced a new quoteCharacter attribute in @CsvSource that allows the user to change the quote character. For consistency between the two features, this commit introduces a new quoteCharacter attribute in @CsvFileSource. The quoteCharacter defaults to a double quote for backward compatibility. Closes #2735
1 parent e27058e commit 4181b9c

File tree

7 files changed

+65
-31
lines changed

7 files changed

+65
-31
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.8.2.adoc

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[[release-notes-5.8.2]]
22
== 5.8.2
33

4-
*Date of Release:* October ❓, 2021
4+
*Date of Release:* November ❓, 2021
55

66
*Scope:*
77

8-
* Text blocks in `@CsvSource` are treated as CSV files
9-
* Custom quote character support in `@CsvSource`
8+
* Text blocks in `@CsvSource` are treated like CSV files
9+
* Custom quote character support in `@CsvSource` and `@CsvFileSource`
1010

1111
For a complete list of all _closed_ issues and pull requests for this release, consult the
1212
link:{junit5-repo}+/milestone/60?closed=1+[5.8.2] milestone page in the JUnit repository on
@@ -24,14 +24,13 @@ No changes.
2424

2525
==== New Features and Improvements
2626

27-
* Text blocks in `@CsvSource` are now treated as complete CSV files, including support for
28-
comments beginning with a `+++#+++` symbol as well as support for new lines within
27+
* Text blocks in `@CsvSource` are now treated like complete CSV files, including support
28+
for comments beginning with a `+++#+++` symbol as well as support for new lines within
2929
quoted strings. See the
3030
<<../user-guide/index.adoc#writing-tests-parameterized-tests-sources-CsvSource, User
3131
Guide>> for details and examples.
32-
* The quote character for _quoted strings_ in `@CsvSource` is now configurable via the new
33-
`quoteCharacter` attribute, which defaults to a single quote (`'`) for backward
34-
compatibility.
32+
* The quote character for _quoted strings_ in `@CsvSource` and `@CsvFileSource` is now
33+
configurable via new `quoteCharacter` attributes in each annotation.
3534

3635

3736
[[release-notes-5.8.2-junit-vintage]]

documentation/src/docs/asciidoc/user-guide/writing-tests.adoc

+8-7
Original file line numberDiff line numberDiff line change
@@ -1344,9 +1344,9 @@ The default delimiter is a comma (`,`), but you can use another character by set
13441344
`String` delimiter instead of a single character. However, both delimiter attributes
13451345
cannot be set simultaneously.
13461346

1347-
By default, `@CsvSource` uses a single quote `'` as its quote character, but this can be
1347+
By default, `@CsvSource` uses a single quote (`'`) as its quote character, but this can be
13481348
changed via the `quoteCharacter` attribute. See the `'lemon, lime'` value in the example
1349-
above and in the table below. An empty, quoted value `''` results in an empty `String`
1349+
above and in the table below. An empty, quoted value (`''`) results in an empty `String`
13501350
unless the `emptyValue` attribute is set; whereas, an entirely _empty_ value is
13511351
interpreted as a `null` reference. By specifying one or more `nullValues`, a custom value
13521352
can be interpreted as a `null` reference (see the `NIL` example in the table below). An
@@ -1395,9 +1395,9 @@ In contrast to CSV records supplied via the `value` attribute, a text block can
13951395
comments. Any line beginning with a `+++#+++` symbol will be treated as a comment and
13961396
ignored. Note, however, that the `+++#+++` symbol must be the first character on the line
13971397
without any leading whitespace. It is therefore recommended that the closing text block
1398-
delimiter `"""` be placed either at the end of the last line of input or on the following
1399-
line, left aligned with the rest of the input (as can be seen in the example below which
1400-
demonstrates formatting similar to a table).
1398+
delimiter (`"""`) be placed either at the end of the last line of input or on the
1399+
following line, left aligned with the rest of the input (as can be seen in the example
1400+
below which demonstrates formatting similar to a table).
14011401

14021402
[source,java,indent=0]
14031403
----
@@ -1458,8 +1458,9 @@ include::{testResourcesDir}/two-column.csv[]
14581458
----
14591459

14601460
In contrast to the default syntax used in `@CsvSource`, `@CsvFileSource` uses a double
1461-
quote `"` as the quote character. See the `"United States of America"` value in the
1462-
example above. An empty, quoted value `""` results in an empty `String` unless the
1461+
quote (`+++"+++`) as the quote character by default, but this can be changed via the
1462+
`quoteCharacter` attribute. See the `"United States of America"` value in the example
1463+
above. An empty, quoted value (`+++""+++`) results in an empty `String` unless the
14631464
`emptyValue` attribute is set; whereas, an entirely _empty_ value is interpreted as a
14641465
`null` reference. By specifying one or more `nullValues`, a custom value can be
14651466
interpreted as a `null` reference. An `ArgumentConversionException` is thrown if the

documentation/src/test/resources/two-column.csv

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Country, reference
1+
Country, Reference
22
Sweden, 1
33
Poland, 2
44
"United States of America", 3

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSource.java

+19-6
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@
3636
* via either {@link #delimiter} or {@link #delimiterString}.
3737
*
3838
* <p>In contrast to the default syntax used in {@code @CsvSource}, {@code @CsvFileSource}
39-
* uses a double quote ({@code "}) as its quote character (see the User Guide for
40-
* examples). An empty, quoted value ({@code ""}) results in an empty {@link String}
41-
* unless the {@link #emptyValue} attribute is set; whereas, an entirely <em>empty</em>
42-
* value is interpreted as a {@code null} reference. By specifying one or more
43-
* {@link #nullValues} a custom value can be interpreted as a {@code null} reference
44-
* (see the User Guide for an example). An
39+
* uses a double quote ({@code "}) as its quote character by default, but this can
40+
* be changed via {@link #quoteCharacter}. An empty, quoted value ({@code ""})
41+
* results in an empty {@link String} unless the {@link #emptyValue} attribute is
42+
* set; whereas, an entirely <em>empty</em> value is interpreted as a {@code null}
43+
* reference. By specifying one or more {@link #nullValues} a custom value can be
44+
* interpreted as a {@code null} reference (see the User Guide for an example). An
4545
* {@link org.junit.jupiter.params.converter.ArgumentConversionException
4646
* ArgumentConversionException} is thrown if the target type of a {@code null}
4747
* reference is a primitive type.
@@ -95,6 +95,19 @@
9595
*/
9696
String lineSeparator() default "\n";
9797

98+
/**
99+
* The quote character to use for <em>quoted strings</em>.
100+
*
101+
* <p>Defaults to a double quote ({@code "}).
102+
*
103+
* <p>You may change the quote character to anything that makes sense for
104+
* your use case.
105+
*
106+
* @since 5.8.2
107+
*/
108+
@API(status = EXPERIMENTAL, since = "5.8.2")
109+
char quoteCharacter() default '"';
110+
98111
/**
99112
* The column delimiter character to use when reading the CSV files.
100113
*

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParserFactory.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ class CsvParserFactory {
2424

2525
private static final String DEFAULT_DELIMITER = ",";
2626
private static final String LINE_SEPARATOR = "\n";
27-
private static final char DOUBLE_QUOTE = '"';
2827
private static final char EMPTY_CHAR = '\0';
2928
private static final boolean COMMENT_PROCESSING_FOR_CSV_FILE_SOURCE = true;
3029

@@ -37,7 +36,7 @@ static CsvParser createParserFor(CsvSource annotation) {
3736

3837
static CsvParser createParserFor(CsvFileSource annotation) {
3938
String delimiter = selectDelimiter(annotation, annotation.delimiter(), annotation.delimiterString());
40-
return createParser(delimiter, annotation.lineSeparator(), DOUBLE_QUOTE, annotation.emptyValue(),
39+
return createParser(delimiter, annotation.lineSeparator(), annotation.quoteCharacter(), annotation.emptyValue(),
4140
annotation.maxCharsPerColumn(), COMMENT_PROCESSING_FOR_CSV_FILE_SOURCE,
4241
annotation.ignoreLeadingAndTrailingWhitespace());
4342
}

junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/CsvFileArgumentsProviderTests.java

+13
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ void providesArgumentsForCarriageReturnAndSemicolon() {
6565
assertThat(arguments).containsExactly(array("foo", "bar"), array("baz", "qux"));
6666
}
6767

68+
@Test
69+
void providesArgumentsWithCustomQuoteCharacter() {
70+
var annotation = csvFileSource()//
71+
.resources("test.csv")//
72+
.quoteCharacter('\'')//
73+
.build();
74+
75+
var arguments = provideArguments(annotation, "foo, 'bar \"and\" baz', qux \n 'lemon lime', banana, apple");
76+
77+
assertThat(arguments).containsExactly(array("foo", "bar \"and\" baz", "qux"),
78+
array("lemon lime", "banana", "apple"));
79+
}
80+
6881
@Test
6982
void providesArgumentsWithStringDelimiter() {
7083
var annotation = csvFileSource()//

junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MockCsvAnnotationBuilder.java

+16-7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ static MockCsvFileSourceBuilder csvFileSource() {
3434

3535
// -------------------------------------------------------------------------
3636

37+
private char quoteCharacter = '\0';
3738
protected char delimiter = '\0';
3839
protected String delimiterString = "";
3940
protected String emptyValue = "";
@@ -46,6 +47,11 @@ private MockCsvAnnotationBuilder() {
4647

4748
protected abstract B getSelf();
4849

50+
B quoteCharacter(char quoteCharacter) {
51+
this.quoteCharacter = quoteCharacter;
52+
return getSelf();
53+
}
54+
4955
B delimiter(char delimiter) {
5056
this.delimiter = delimiter;
5157
return getSelf();
@@ -84,7 +90,10 @@ static class MockCsvSourceBuilder extends MockCsvAnnotationBuilder<CsvSource, Mo
8490

8591
private String[] lines = new String[0];
8692
private String textBlock = "";
87-
private char quoteCharacter = '\'';
93+
94+
private MockCsvSourceBuilder() {
95+
super.quoteCharacter = '\'';
96+
}
8897

8998
@Override
9099
protected MockCsvSourceBuilder getSelf() {
@@ -101,16 +110,12 @@ MockCsvSourceBuilder textBlock(String textBlock) {
101110
return this;
102111
}
103112

104-
MockCsvSourceBuilder quoteCharacter(char quoteCharacter) {
105-
this.quoteCharacter = quoteCharacter;
106-
return this;
107-
}
108-
109113
@Override
110114
CsvSource build() {
111115
var annotation = mock(CsvSource.class);
112116

113117
// Common
118+
when(annotation.quoteCharacter()).thenReturn(super.quoteCharacter);
114119
when(annotation.delimiter()).thenReturn(super.delimiter);
115120
when(annotation.delimiterString()).thenReturn(super.delimiterString);
116121
when(annotation.emptyValue()).thenReturn(super.emptyValue);
@@ -121,7 +126,6 @@ CsvSource build() {
121126
// @CsvSource
122127
when(annotation.value()).thenReturn(this.lines);
123128
when(annotation.textBlock()).thenReturn(this.textBlock);
124-
when(annotation.quoteCharacter()).thenReturn(this.quoteCharacter);
125129

126130
return annotation;
127131
}
@@ -136,6 +140,10 @@ static class MockCsvFileSourceBuilder extends MockCsvAnnotationBuilder<CsvFileSo
136140
private String lineSeparator = "\n";
137141
private int numLinesToSkip = 0;
138142

143+
private MockCsvFileSourceBuilder() {
144+
super.quoteCharacter = '"';
145+
}
146+
139147
@Override
140148
protected MockCsvFileSourceBuilder getSelf() {
141149
return this;
@@ -171,6 +179,7 @@ CsvFileSource build() {
171179
var annotation = mock(CsvFileSource.class);
172180

173181
// Common
182+
when(annotation.quoteCharacter()).thenReturn(super.quoteCharacter);
174183
when(annotation.delimiter()).thenReturn(super.delimiter);
175184
when(annotation.delimiterString()).thenReturn(super.delimiterString);
176185
when(annotation.emptyValue()).thenReturn(super.emptyValue);

0 commit comments

Comments
 (0)