Skip to content

Commit 39cfd6e

Browse files
authored
Merge pull request #713 from rkampani/feature/704-fixJunitsJdk24
Error in running the zerocode tests in JDK23/21 #704
2 parents 818fec0 + fb49860 commit 39cfd6e

26 files changed

+361
-161
lines changed

core/pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,6 @@
112112
<groupId>com.google.classpath-explorer</groupId>
113113
<artifactId>classpath-explorer</artifactId>
114114
</dependency>
115-
<dependency>
116-
<groupId>org.jukito</groupId>
117-
<artifactId>jukito</artifactId>
118-
<scope>test</scope>
119-
</dependency>
120115
<dependency>
121116
<groupId>com.google.inject</groupId>
122117
<artifactId>guice</artifactId>
@@ -146,6 +141,11 @@
146141
<artifactId>junit</artifactId>
147142
<!--<scope>test</scope>--> <!-- added with compile scope. Do not change this -->
148143
</dependency>
144+
<dependency>
145+
<groupId>org.mockito</groupId>
146+
<artifactId>mockito-core</artifactId>
147+
<scope>test</scope>
148+
</dependency>
149149
<dependency>
150150
<groupId>org.apache.httpcomponents</groupId>
151151
<artifactId>httpclient</artifactId>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.jsmart.zerocode.core.di.module;
2+
3+
import com.google.gson.TypeAdapter;
4+
import com.google.gson.stream.JsonReader;
5+
import com.google.gson.stream.JsonWriter;
6+
import java.io.IOException;
7+
import java.util.Optional;
8+
9+
public class OptionalTypeAdapter<T> extends TypeAdapter<Optional<T>> {
10+
private final TypeAdapter<T> delegate;
11+
12+
public OptionalTypeAdapter(TypeAdapter<T> delegate) {
13+
this.delegate = delegate;
14+
}
15+
16+
@Override
17+
public void write(JsonWriter out, Optional<T> value) throws IOException {
18+
if (value == null || !value.isPresent()) {
19+
out.nullValue();
20+
} else {
21+
delegate.write(out, value.get());
22+
}
23+
}
24+
25+
@Override
26+
public Optional<T> read(JsonReader in) throws IOException {
27+
if (in.peek() == com.google.gson.stream.JsonToken.NULL) {
28+
in.nextNull();
29+
return Optional.empty();
30+
} else {
31+
return Optional.ofNullable(delegate.read(in));
32+
}
33+
}
34+
35+
public static <T> TypeAdapter<Optional<T>> factory(TypeAdapter<T> delegate) {
36+
return new OptionalTypeAdapter<>(delegate);
37+
}
38+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.jsmart.zerocode.core.di.module;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.TypeAdapter;
5+
import com.google.gson.TypeAdapterFactory;
6+
import com.google.gson.reflect.TypeToken;
7+
8+
import java.lang.reflect.ParameterizedType;
9+
import java.lang.reflect.Type;
10+
import java.util.Optional;
11+
12+
public class OptionalTypeAdapterFactory implements TypeAdapterFactory {
13+
14+
@Override
15+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
16+
if (!Optional.class.isAssignableFrom(typeToken.getRawType())) {
17+
return null;
18+
}
19+
20+
Type type = typeToken.getType();
21+
if (type instanceof ParameterizedType) {
22+
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
23+
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
24+
return (TypeAdapter<T>) OptionalTypeAdapter.factory(elementAdapter);
25+
}
26+
27+
return null;
28+
}
29+
}

core/src/main/java/org/jsmart/zerocode/core/di/provider/GsonSerDeProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,20 @@
1111
import jakarta.inject.Provider;
1212
import org.apache.kafka.common.header.Headers;
1313
import org.apache.kafka.common.header.internals.RecordHeaders;
14+
import org.jsmart.zerocode.core.di.module.OptionalTypeAdapterFactory;
1415

1516
import java.io.IOException;
1617
import java.util.HashMap;
1718
import java.util.Map;
19+
import java.util.Optional;
1820

1921
public class GsonSerDeProvider implements Provider<Gson> {
2022

2123
@Override
2224
public Gson get() {
2325
return new GsonBuilder()
2426
.registerTypeAdapterFactory(KafkaHeadersAdapter.FACTORY)
27+
.registerTypeAdapterFactory(new OptionalTypeAdapterFactory())
2528
.create();
2629
}
2730

core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.JSON_CONTENT;
6767
import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.deepTypeCast;
6868
import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.fieldTypes;
69+
import static org.jsmart.zerocode.core.utils.HelperJsonUtils.readJsonPath;
6970
import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadAbsoluteProperties;
7071
import static org.jsmart.zerocode.core.utils.SmartUtils.checkDigNeeded;
7172
import static org.jsmart.zerocode.core.utils.SmartUtils.getJsonFilePhToken;
@@ -138,7 +139,7 @@ public String resolveJsonPaths(String jsonString, String scenarioState) {
138139
* Use escapeJava, do not use escapeJavaScript, as escapeJavaScript also escapes single quotes
139140
* which in turn throws Jackson Exception
140141
*/
141-
String escapedString = escapeJava(JsonPath.read(scenarioState, thisPath));
142+
String escapedString = escapeJava(readJsonPath(scenarioState, thisPath, String.class));
142143
paramMap.put(thisPath, escapedString);
143144

144145
} else if (thisPath.matches(LEAF_VAL_REGEX) || thisPath.endsWith($VALUE)) {
@@ -154,7 +155,7 @@ public String resolveJsonPaths(String jsonString, String scenarioState) {
154155

155156
} else {
156157

157-
paramMap.put(thisPath, JsonPath.read(scenarioState, thisPath));
158+
paramMap.put(thisPath, readJsonPath(scenarioState, thisPath, String.class));
158159

159160
}
160161
}
@@ -448,7 +449,7 @@ void resolveLeafOnlyNodeValue(String scenarioState, Map<String, String> paramMap
448449
String actualPath = thisPath.substring(0, thisPath.indexOf($VALUE));
449450
int index = findArrayIndex(thisPath, actualPath);
450451

451-
List<String> leafValuesAsArray = JsonPath.read(scenarioState, actualPath);
452+
List<String> leafValuesAsArray = readJsonPath(scenarioState, actualPath, List.class);
452453
paramMap.put(thisPath, leafValuesAsArray.get(index));
453454
}
454455

core/src/main/java/org/jsmart/zerocode/core/kafka/helper/KafkaConsumerHelper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import static org.jsmart.zerocode.core.kafka.KafkaConstants.PROTO;
6868
import static org.jsmart.zerocode.core.kafka.KafkaConstants.RAW;
6969
import static org.jsmart.zerocode.core.kafka.common.KafkaCommonUtils.resolveValuePlaceHolders;
70+
import static org.jsmart.zerocode.core.utils.HelperJsonUtils.readJsonPath;
7071
import static org.jsmart.zerocode.core.utils.SmartUtils.prettyPrintJson;
7172

7273
public class KafkaConsumerHelper {
@@ -387,7 +388,7 @@ public static String prepareResult(ConsumerLocalConfigs testConfigs,
387388

388389
// Optional filter applied. if not supplied, original result is returned as response
389390
if (testConfigs != null && testConfigs.getFilterByJsonPath() != null) {
390-
String filteredResult = JsonPath.read(result, testConfigs.getFilterByJsonPath()).toString();
391+
String filteredResult = readJsonPath(result, testConfigs.getFilterByJsonPath(), String.class).toString();
391392
List<ConsumerJsonRecord> filteredRecords = objectMapper.readValue(filteredResult, List.class);
392393
result = prettyPrintJson(objectMapper.writeValueAsString(new ConsumerJsonRecords(filteredRecords)));
393394
}

core/src/main/java/org/jsmart/zerocode/core/utils/HelperJsonUtils.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@
55
import com.fasterxml.jackson.databind.ObjectMapper;
66
import com.jayway.jsonpath.JsonPath;
77
import com.jayway.jsonpath.PathNotFoundException;
8-
import java.util.ArrayList;
9-
import java.util.Arrays;
10-
import java.util.List;
11-
import java.util.stream.Collectors;
12-
138
import org.apache.commons.lang3.StringUtils;
149
import org.jsmart.zerocode.core.di.provider.ObjectMapperProvider;
1510
import org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher;
1611
import org.slf4j.LoggerFactory;
1712

1813
import java.io.IOException;
14+
import java.util.ArrayList;
15+
import java.util.Arrays;
1916
import java.util.HashMap;
17+
import java.util.List;
2018
import java.util.Map;
19+
import java.util.stream.Collectors;
2120

2221
import static org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher.aMatchingMessage;
2322
import static org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher.aNotMatchingMessage;
@@ -161,4 +160,36 @@ public static Object readJsonPathOrElseNull(String requestJson, String jsonPath)
161160
return null;
162161
}
163162
}
163+
164+
/**
165+
* Reads a value from a JSON string using a JSON Path expression and converts it to the specified type.
166+
* <p>
167+
* This method extracts a value from the provided JSON string using the given JSON Path expression
168+
* and converts it to the target type specified by {@code clazz}. It is designed to be compatible
169+
* with JDK 9 and higher, where generic type inference is stricter. The explicit use of {@code Class<T>}
170+
* ensures proper type handling during conversion, avoiding issues with type erasure or inference.
171+
* </p>
172+
* <p>
173+
* If the JSON Path does not exist, or the value is null, the method logs a warning and returns {@code null}.
174+
* Any other errors during JSON Path evaluation or type conversion are logged as errors, and {@code null} is returned.
175+
* </p>
176+
*/
177+
public static <T> T readJsonPath(final String requestJson, final String jsonPath, final Class<T> clazz) {
178+
try {
179+
// Read the raw value from JSON Path
180+
final Object result = JsonPath.read(requestJson, jsonPath);
181+
if (result == null) {
182+
LOGGER.warn("JSON Path {} returned null.", jsonPath);
183+
return null;
184+
}
185+
// Convert the result to the target class
186+
return mapper.convertValue(result, clazz);
187+
} catch (final PathNotFoundException pEx) {
188+
LOGGER.warn("No {} was present in the request. Returned null.", jsonPath);
189+
return null;
190+
} catch (Exception e) {
191+
LOGGER.error("Error converting JSON Path {} to type {}: {}", jsonPath, clazz.getSimpleName(), e.getMessage());
192+
return null;
193+
}
194+
}
164195
}

core/src/test/java/org/jsmart/zerocode/core/db/DbCsvLoaderTest.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
package org.jsmart.zerocode.core.db;
22

3-
import static org.hamcrest.CoreMatchers.equalTo;
4-
import static org.hamcrest.MatcherAssert.assertThat;
5-
import static org.junit.Assert.assertThrows;
3+
import com.google.inject.Inject;
4+
import org.junit.Test;
65

76
import java.sql.SQLException;
87
import java.util.Arrays;
98
import java.util.List;
109
import java.util.Map;
1110

1211
import org.jsmart.zerocode.core.di.provider.CsvParserProvider;
13-
import org.jukito.JukitoRunner;
14-
import org.junit.Test;
15-
import org.junit.runner.RunWith;
16-
17-
import com.google.inject.Inject;
12+
import static org.hamcrest.CoreMatchers.equalTo;
13+
import static org.hamcrest.MatcherAssert.assertThat;
14+
import static org.junit.Assert.assertThrows;
1815

19-
@RunWith(JukitoRunner.class)
2016
public class DbCsvLoaderTest extends DbTestBase{
2117

2218
private DbCsvLoader loader;

core/src/test/java/org/jsmart/zerocode/core/db/DbSqlRunnerTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
import java.util.Map;
1010

1111
import org.apache.commons.dbutils.QueryRunner;
12-
import org.jukito.JukitoRunner;
12+
13+
1314
import org.junit.Before;
1415
import org.junit.Test;
15-
import org.junit.runner.RunWith;
1616

17-
@RunWith(JukitoRunner.class)
17+
1818
public class DbSqlRunnerTest extends DbTestBase {
1919

2020
@Before

core/src/test/java/org/jsmart/zerocode/core/db/DbTestBase.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
11
package org.jsmart.zerocode.core.db;
22

3-
import java.sql.Connection;
4-
import java.sql.DriverManager;
5-
import java.sql.SQLException;
6-
import java.util.List;
7-
import java.util.Map;
8-
3+
import com.google.inject.AbstractModule;
4+
import com.google.inject.Inject;
5+
import com.google.inject.name.Named;
96
import org.apache.commons.dbutils.DbUtils;
107
import org.jsmart.zerocode.core.di.main.ApplicationMainModule;
11-
import org.jukito.TestModule;
8+
import org.jsmart.zerocode.core.guice.ZeroCodeGuiceTestRule;
129
import org.junit.After;
1310
import org.junit.Before;
11+
import org.junit.Rule;
1412

15-
import com.google.inject.Inject;
16-
import com.google.inject.name.Named;
13+
import java.sql.Connection;
14+
import java.sql.DriverManager;
15+
import java.sql.SQLException;
16+
import java.util.List;
17+
import java.util.Map;
1718

1819
/**
1920
* Base class for the unit DB test classes: manages connections,
2021
* execution of queries and DBMS specific features
2122
*/
2223
public abstract class DbTestBase {
24+
@Rule
25+
public ZeroCodeGuiceTestRule guiceRule = new ZeroCodeGuiceTestRule(this, DbTestBase.ZeroCodeTestModule.class);
26+
2327
// Subclasses must use JukitoRunner
24-
public static class JukitoModule extends TestModule {
28+
public static class ZeroCodeTestModule extends AbstractModule {
2529
@Override
26-
protected void configureTest() {
30+
protected void configure() {
2731
ApplicationMainModule applicationMainModule = new ApplicationMainModule("db_test.properties");
2832
install(applicationMainModule);
2933
}

0 commit comments

Comments
 (0)