Deserializing data which can be object or boolean - jaxb

I'm using maven and jersey with the following dependencies
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics-annotate</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>0.9.1</version>
</dependency>
I'm using the
maven-jaxb2-plugin
plugin to generate classes from an xsd.
I'm trying to deserialize json which can be received in two ways:
{
"config": {
"field1": 1,
"field2": 2,
"object1": {
.
.
}
}
}
or
{
"config": false
}
For the latter I would expect
{
"config": {}
}
but this is not the case nor do I have influence on that.
When I deserialize this I get an exception
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.my.model.ConfigMap out of VALUE_FALSE token
It's clear why I get this exception.
I've been trying to use a custom deserializer to get around this but unsuccessful.
public class ConfigTypeDeserializer extends JsonDeserializer<ConfigType> {
#Override
public ConfigType deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
ObjectCodec cd = jsonParser.getCodec();
JsonNode node = cd.readTree(jsonParser);
ConfigType object = new ObjectFactory().createConfigType();
if (node.isBoolean()) {
return object;
}
// now I know its not a weird response so now I want to deserialize as usual
return object;
}
}
So I check if I see the weird response and if so I return an empty object and if not I want to return the object by deserializing it a usual because the object is too complex to completely build it manually in this deserializer. But I did not find a way to do this. I can e.g. call
cd.readValue(jsonParser,ConfigType.class);
but this does produce a ConfigType object but the contents are not correct. I'm not getting the expected object types.
So I want to know if it is possible to use (or continue) the existing deserialization code from my custom deserializer.
If that is not possible, is there another way to do this?

I found a really neat library
https://github.com/jonpeterson/jackson-module-json-interceptor
With maven:
<dependency>
<groupId>com.github.jonpeterson</groupId>
<artifactId>jackson-module-json-interceptor</artifactId>
<version>1.0.0</version>
</dependency>
which enabled me to intercept the deserialisation process just before it deserialises the object.
You can use this annotation
#JsonInterceptors(beforeDeserialization = {
MyDeserializationInterceptor.class
})
public class MyProblematicObject {
..
}
In my interceptor class I can do this
public class MyDeserializationInterceptor implements JsonInterceptor {
#Override
public JsonNode intercept(JsonNode node, JsonNodeFactory nodeFactory) {
JsonNode config = node.findValue("config");
// convert boolean to object
if (node.isObject() && config.isBoolean()) {
ObjectNode objectNode = nodeFactory.objectNode();
((ObjectNode)node).set("config",objectNode);
}
return node;
}
}
And the deserialisation process happily continues :)
Thank you Jon Peterson !!

Related

Couldn't load plugin class: com.qmetry.qaf.automation.cucumber.QAFCucumberPlugin. It does not implement cucumber.api.Plugin

I planning to use qaf-cucumber library for scenario outlines testdata with external files.
Repo: https://github.com/qmetry/qaf-cucumber
Branch: cucumber-4
When I run my runner class I'm getting below error: Couldn't load plugin class: com.qmetry.qaf.automation.cucumber.QAFCucumberPlugin. It does not implement cucumber.api.Plugin
pom.xml:
<dependencies>
<dependency><groupId>com.qmetry</groupId><artifactId>qaf</artifactId><version>2.1.15</version></dependency>
<dependency><groupId>com.qmetry</groupId><artifactId>qaf-cucumber</artifactId><version>2.1.15-beta-1</version></dependency>
<dependency><groupId>io.cucumber</groupId><artifactId>cucumber-java</artifactId><version>4.5.1</version></dependency>
<dependency><groupId>io.cucumber</groupId><artifactId>cucumber-junit</artifactId><version>4.5.1</version><scope>test</scope></dependency>
<dependency><groupId>io.cucumber</groupId><artifactId>cucumber-plugin</artifactId><version>5.1.3</version></dependency>
<dependency><groupId>io.cucumber</groupId><artifactId>cucumber-core</artifactId><version>4.5.1</version></dependency>
<dependency><groupId>com.aventstack</groupId><artifactId>extentreports-cucumber4-adapter</artifactId><version>1.0.8</version><scope>compile</scope></dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version><scope>compile</scope></dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version><scope>compile</scope></dependency>
<dependency><groupId>io.cucumber</groupId><artifactId>cucumber-junit</artifactId><version>4.5.1</version><scope>compile</scope></dependency>
<dependency><groupId>net.masterthought</groupId><artifactId>cucumber-reporting</artifactId><version>4.10.0</version></dependency>
</dependencies>
RunnerClass:
import io.cucumber.junit.CucumberOptions;
import io.cucumber.junit.Cucumber;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features="src/main/java/Features",
glue={"StepDefs"},
monochrome=true,
plugin = { "pretty", "com.qmetry.qaf.automation.cucumber.QAFCucumberPlugin"}
)
public class JunitRunner {
}
feature file:
#smoke
Scenario Outline: Search Keyword using data from file
Given I am on Google Search Page
When I search for "<searchKey>"
Examples:{'datafile':'resources/testdata.json'}
StepDefs:
import com.qmetry.qaf.automation.step.QAFTestStep;
import com.qmetry.qaf.automation.step.QAFTestStepProvider;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
public class GoogleClass {
#Given("I am on Google Search Page")
#QAFTestStep(description="I am on Google Search Page")
public void iAmOnGoogleSearchPage(){
System.out.println("I am on Google Search Page --- New");
}
#When("^I search for \"([^\"]*)\"$")
#QAFTestStep(description = "I search for {0}")
public void iSearchFor(String s) {
System.out.println("I search for " + s);
}
#QAFTestStep(description="it should have {0} in search results")
public void itShouldHave_inSearchResults(String s) {
System.out.printf("it should have %s in search results\n", s);
}
}
Exception:
cucumber.runtime.CucumberException: Couldn't load plugin class: com.qmetry.qaf.automation.cucumber.QAFCucumberPlugin. It does not implement cucumber.api.Plugin
at cucumber.runtime.formatter.PluginFactory.loadClass(PluginFactory.java:178)
at cucumber.runtime.formatter.PluginFactory.pluginClass(PluginFactory.java:165)
at cucumber.runtime.formatter.PluginFactory.getPluginClass(PluginFactory.java:222)
at cucumber.runtime.formatter.PluginFactory.isStepDefinitionReporterName(PluginFactory.java:205)
at io.cucumber.core.options.RuntimeOptionsBuilder$ParsedPluginData.addPluginName(RuntimeOptionsBuilder.java:218)
at io.cucumber.core.options.RuntimeOptionsBuilder.addPluginName(RuntimeOptionsBuilder.java:73)
at io.cucumber.core.options.CucumberOptionsAnnotationParser.addPlugins(CucumberOptionsAnnotationParser.java:96)
at io.cucumber.core.options.CucumberOptionsAnnotationParser.parse(CucumberOptionsAnnotationParser.java:51)
at io.cucumber.junit.Cucumber.<init>(Cucumber.java:91)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:70)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:37)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:70)
at org.junit.internal.requests.ClassRequest.createRunner(ClassRequest.java:28)
at org.junit.internal.requests.MemoizingRequest.getRunner(MemoizingRequest.java:19)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:50)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
For cucumber-4 support you need to use qaf-cucumber4 dependency instead of qaf-cucumber.
I am able to find everything working with following setup:
POM:
<dependencies>
<dependency>
<groupId>com.qmetry</groupId>
<artifactId>qaf</artifactId>
<version>2.1.15</version>
</dependency>
<dependency>
<groupId>com.qmetry</groupId>
<artifactId>qaf-cucumber4</artifactId>
<version>2.1.15</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>4.8.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>4.8.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Runner:
package com.example;
import io.cucumber.junit.CucumberOptions;
import io.cucumber.junit.Cucumber;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features="Features",
glue={"com.example.common"},
monochrome=true,
plugin = {"com.qmetry.qaf.automation.cucumber.QAFCucumberPlugin","pretty"}
)
public class JunitRunner {
}
Feature file:
Feature: Test cucumber
#smoke
Scenario: Search Keyword
Given I am on Google Search Page
When I search for "qaf"
#smoke
#datafile:resources/testdata.json
Scenario Outline: Search Keyword using data from file
Given I am on Google Search Page
When I search for "<searchKey>"
data file:
[
{
"searchKey": "qaf selenium"
},
{
"searchKey": "selenium ecosystem frameworks"
}
]

weld-junit5 4.0.0.CR1 namespace confusion

I'm a bit confused about the namespace usage for #Inject, #Produces etc. with weld-junit5 4.0.0.CR2. I'm toying around with JakartaEE 8 + Java 17 and weld-junit5 3.x got upset so I guess 4.x is the way to go?
The problem is that with a test class like
#EnableWeld
public class TestNotificationDao {
#WeldSetup
public WeldInitiator weld = WeldInitiator.of(NotificationDao.class, NotificationDaoImpl.class, TestNotificationDao.class);
#Inject
private NotificationDao notificationDao;
public TestNotificationDao() {
}
#Produces
static DataSource getDataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setURL("jdbc:mysql://localhost:3306/sps?useSSL=false");
dataSource.setUser("sps");
dataSource.setPassword("sps");
dataSource.setDatabaseName("sps");
return dataSource;
}
#Test
public void testLoadQueuedNotifications() {
Collection<QueuedNotification> notifications = notificationDao.getQueuedNotifications(1);
assertEquals(1, notifications.size());
}
}
If I use javax.inject.Inject, the notificationDao is null and If I use jakarta.inject.Inject (and Produces), notificationDao is injected but the #javax.inject.Inject private DataSource dataSource inside the NotificationDaoImpl is null.
Solved by moving to the
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>

How to set up & clean up a resource in Spock for a test suite

I have this code in my JUnit suite:
#RunWith(Suite.class)
#Suite.SuiteClasses({ MyJavaTest.class, MyAnotherJavaTest.class })
public class MyIntegrationSuite {
#BeforeClass
public static void beforeTests() throws Exception {
Container.start();
}
#AfterClass
public static void afterTests() throws Exception {
Container.stop();
}
}
I want to rewrite it to Spock, but I can't find any way to do a global setup, only setupSpec() and setup() which is not enough, since I have multiple specifications and I want to start a Container only once.
I tried leaving the suite as it is and passing Spock specifications to it, but the spock tests are skipped entirely (when I add extends Specifications). It's probably, because Specification has #RunWith(Sputnik) and it doesn't play with #RunWith(Suite), but I'm not sure how to get around that.
Is there any way to do a global setup with Spock or execute Spock specifications from JUnit suite?
My pom.xml (stubs are there, because I'm mixing java and groovy code):
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>${buildhelper.plugin.version}</version>
<executions>
<execution>
<id>add-groovy-test-source</id>
<phase>test</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src/test/groovy</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>${gmavenplus.plugin.version}</version>
<executions>
<execution>
<goals>
<goal>generateTestStubs</goal>
<goal>compileTests</goal>
<goal>removeTestStubs</goal>
</goals>
</execution>
</executions>
</plugin>
In general, Spock is a JUnit After all (that's why surefire plugin can run it without any additional hassle), so all approaches to the Suites should work, although I haven't used this feature.
In addition, If you have a "heavy" shared resource, then you might try the following approach:
abstract class SharedResourceSupport extends Specification {
def static sharedSource = new SharedSource()
}
class SharedSource {
public SharedSource() {
println "Created shared source. Its our heavy resource!"
}
}
class SpockTest1 extends SharedResourceSupport {
def "sample test" () {
expect:
sharedSource != null
}
}
class SpockTest2 extends SharedResourceSupport {
def "another test" () {
expect:
sharedSource != null
}
}
Note that the shared resource is defined with "static", so that it will be created only once when the first test will access it.
As different strategies to deal with this: You might want to consider traits, if your test already inherits from another class, or expose shared resource as a Singleton so that it will guarantee that only one instance exists.
Try to run both tests and you'll see that that the line "Created shared source..." is called only once
The praise for the right answer belongs to Mark Bramnik when he wrote:
In general, Spock is a JUnit After all (that's why surefire plugin can run it without any additional hassle), so all approaches to the Suites should work
While this is the correct answer, the sample code in his answer refers to another scenario. So I am going to provide it here for reference. I do not expect this answer to get accepted, but if others read it they also should find sample code explaining how to use the feature in Spock:
Sample Spock tests:
package de.scrum_master.stackoverflow
class FooTest extends Specification {
def test() {
expect:
println "FooTest"
}
}
package de.scrum_master.stackoverflow
class BarTest extends Specification {
def test() {
expect:
println "BarTest"
}
}
Test suite:
package de.scrum_master.stackoverflow
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.runner.RunWith
import org.junit.runners.Suite
import spock.lang.Specification
#RunWith(Suite.class)
#Suite.SuiteClasses([FooTest, BarTest])
class SampleTestSuite {
#BeforeClass
static void beforeTests() throws Exception {
println "Before suite"
}
#AfterClass
static void afterTests() throws Exception {
println "After suite"
}
}
Console log:
Before suite
FooTest
BarTest
After suite
Actually, it is possible in spock, assuming your code could be static.
To perform one-time initialization (spinup a container, start a test zookeeper cluster or whatever), just create a static singleton holder like the following:
class Postgres {
private static PostgresContainer container
static void init() {
if (container != null)
return
container = new PostgresContainer()
container.start()
}
static void destroy() {
if (container == null)
return
container.stop()
container = null
}
}
Then you need an abstract class for your integration tests, something like:
class IntegrationTest extends Specification {
def setup() {
init()
}
static void init () {
Postgres.init()
}
def cleanup() {
// do whatever you need
}
static void destroy() {
Postgres.destroy()
}
}
Now, cleanup is a little bit tricky - specially if you have some non-daemon threads preventing jvm from shutting down. This might cause your test suite to hang.
You can either use shutdownHoooks or use the spock AbstractGlobalExtension mechanism.
You can actually execute some code right after spock executes all the specs.
For our postgres scenario, we'd have something like:
class IntegrationTestCleanup extends AbstractGlobalExtension {
#Override
void stop() {
IntegrationTest.destroy()
}
}
There's one missing puzzle to make it work - you need to provide a special file under src/test/resources/META-INF.services/org.spockframework.runtime.extension.IGlobalExtension that references your extension. That file should contain a single line pointing to your extension, like
com.example.IntegrationTestCleanup
This will make spock recognize it. Keep in mind it will be executed in dedicated spock thread.
I do realize it kind of duplicates the accepted answer, but I was recently struggling with performing a global cleanup in spock so I thought it could be useful.

Can I obtain method parameter name in Groovy? [duplicate]

If I have a class like this:
public class Whatever
{
public void aMethod(int aParam);
}
is there any way to know that aMethod uses a parameter named aParam, that is of type int?
In Java 8 you can do the following:
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
public final class Methods {
public static List<String> getParameterNames(Method method) {
Parameter[] parameters = method.getParameters();
List<String> parameterNames = new ArrayList<>();
for (Parameter parameter : parameters) {
if(!parameter.isNamePresent()) {
throw new IllegalArgumentException("Parameter names are not present!");
}
String parameterName = parameter.getName();
parameterNames.add(parameterName);
}
return parameterNames;
}
private Methods(){}
}
So for your class Whatever we can do a manual test:
import java.lang.reflect.Method;
public class ManualTest {
public static void main(String[] args) {
Method[] declaredMethods = Whatever.class.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getName().equals("aMethod")) {
System.out.println(Methods.getParameterNames(declaredMethod));
break;
}
}
}
}
which should print [aParam] if you have passed -parameters argument to your Java 8 compiler.
For Maven users:
<properties>
<!-- PLUGIN VERSIONS -->
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
<!-- OTHER PROPERTIES -->
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<!-- Original answer -->
<compilerArgument>-parameters</compilerArgument>
<!-- Or, if you use the plugin version >= 3.6.2 -->
<parameters>true</parameters>
<testCompilerArgument>-parameters</testCompilerArgument>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
For more information see following links:
Official Java Tutorial: Obtaining Names of Method Parameters
JEP 118: Access to Parameter Names at Runtime
Javadoc for Parameter class
To summarize:
getting parameter names is possible if debug information is included during compilation. See this answer for more details
otherwise getting parameter names is not possible
getting parameter type is possible, using method.getParameterTypes()
For the sake of writing autocomplete functionality for an editor (as you stated in one of the comments) there are a few options:
use arg0, arg1, arg2 etc.
use intParam, stringParam, objectTypeParam, etc.
use a combination of the above - the former for non-primitive types, and the latter for primitive types.
don't show argument names at all - just the types.
The Paranamer library was created to solve this same problem.
It tries to determine method names in a few different ways. If the class was compiled with debugging it can extract the information by reading the bytecode of the class.
Another way is for it to inject a private static member into the bytecode of the class after it is compiled, but before it is placed in a jar. It then uses reflection to extract this information from the class at runtime.
https://github.com/paul-hammant/paranamer
I had problems using this library, but I did get it working in the end. I'm hoping to report the problems to the maintainer.
see org.springframework.core.DefaultParameterNameDiscoverer class
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Yes.
Code must be compiled with Java 8 compliant compiler with option to store formal parameter names turned on (-parameters option).
Then this code snippet should work:
Class<String> clz = String.class;
for (Method m : clz.getDeclaredMethods()) {
System.err.println(m.getName());
for (Parameter p : m.getParameters()) {
System.err.println(" " + p.getName());
}
}
See java.beans.ConstructorProperties, it's an annotation designed for doing exactly this.
It is possible and Spring MVC 3 does it, but I didn't take the time to see exactly how.
The matching of method parameter names
to URI Template variable names can
only be done if your code is compiled
with debugging enabled. If you do have
not debugging enabled, you must
specify the name of the URI Template
variable name in the #PathVariable
annotation in order to bind the
resolved value of the variable name to
a method parameter. For example:
Taken from the spring documentation
While it is not possible (as others have illustrated), you could use an annotation to carry over the parameter name, and obtain that though reflection.
Not the cleanest solution, but it gets the job done. Some webservices actually do this to keep parameter names (ie: deploying WSs with glassfish).
So you should be able to do:
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
But you'll probably get a list like so:
["int : arg0"]
I believe this will be fixed in Groovy 2.5+
So currently, the answer is:
If it's a Groovy class, then no, you can't get the name, but you should be able to in the future.
If it's a Java class compiled under Java 8, you should be able to.
See also:
http://openjdk.java.net/jeps/118
https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
For every method, then something like:
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
if you use the eclipse, see the bellow image to allow the compiler to store the information about method parameters
As #Bozho stated, it is possible to do it if debug information is included during compilation.
There's a good answer here...
How to get the parameter names of an object's constructors (reflection)? by
#AdamPaynter
...using the ASM library. I put together an example showing how you can achieve your goal.
First of all, start with a pom.xml with these dependencies.
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
Then, this class should do what you want. Just invoke the static method getParameterNames().
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
public class ArgumentReflection {
/**
* Returns a list containing one parameter name for each argument accepted
* by the given constructor. If the class was compiled with debugging
* symbols, the parameter names will match those provided in the Java source
* code. Otherwise, a generic "arg" parameter name is generated ("arg0" for
* the first argument, "arg1" for the second...).
*
* This method relies on the constructor's class loader to locate the
* bytecode resource that defined its class.
*
* #param theMethod
* #return
* #throws IOException
*/
public static List<String> getParameterNames(Method theMethod) throws IOException {
Class<?> declaringClass = theMethod.getDeclaringClass();
ClassLoader declaringClassLoader = declaringClass.getClassLoader();
Type declaringType = Type.getType(declaringClass);
String constructorDescriptor = Type.getMethodDescriptor(theMethod);
String url = declaringType.getInternalName() + ".class";
InputStream classFileInputStream = declaringClassLoader.getResourceAsStream(url);
if (classFileInputStream == null) {
throw new IllegalArgumentException(
"The constructor's class loader cannot find the bytecode that defined the constructor's class (URL: "
+ url + ")");
}
ClassNode classNode;
try {
classNode = new ClassNode();
ClassReader classReader = new ClassReader(classFileInputStream);
classReader.accept(classNode, 0);
} finally {
classFileInputStream.close();
}
#SuppressWarnings("unchecked")
List<MethodNode> methods = classNode.methods;
for (MethodNode method : methods) {
if (method.name.equals(theMethod.getName()) && method.desc.equals(constructorDescriptor)) {
Type[] argumentTypes = Type.getArgumentTypes(method.desc);
List<String> parameterNames = new ArrayList<String>(argumentTypes.length);
#SuppressWarnings("unchecked")
List<LocalVariableNode> localVariables = method.localVariables;
for (int i = 1; i <= argumentTypes.length; i++) {
// The first local variable actually represents the "this"
// object if the method is not static!
parameterNames.add(localVariables.get(i).name);
}
return parameterNames;
}
}
return null;
}
}
Here's an example with a unit test.
public class ArgumentReflectionTest {
#Test
public void shouldExtractTheNamesOfTheParameters3() throws NoSuchMethodException, SecurityException, IOException {
List<String> parameterNames = ArgumentReflection
.getParameterNames(Clazz.class.getMethod("callMe", String.class, String.class));
assertEquals("firstName", parameterNames.get(0));
assertEquals("lastName", parameterNames.get(1));
assertEquals(2, parameterNames.size());
}
public static final class Clazz {
public void callMe(String firstName, String lastName) {
}
}
}
You can find the complete example on GitHub
Caveats
I slightly changed the original solution from #AdamPaynter to make it work for Methods. If I properly understood, his solution works only with constructors.
This solution does not work with static methods. This is becasue in this case the number of arguments returned by ASM is different, but it something that can be easily fixed.
You can't tell the name of the argument used.
You can retrieve the method signature with reflection and detect its argument types, however. Check getParameterTypes().
Parameter names are only useful to the compiler. When the compiler generates a class file, the parameter names are not included - a method's argument list only consists of the number and types of its arguments. So it would be impossible to retrieve the parameter name using reflection (as tagged in your question) - it doesn't exist anywhere.
However, if the use of reflection is not a hard requirement, you can retrieve this information directly from the source code (assuming you have it).
To add my 2 cents; parameter info is available in a class file "for debugging" when you use javac -g to compile the source. And it is available to APT but you'll need an annotation so no use to you. (Somebody discussed something similar 4-5 years ago here: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Overall in-short you can't get it unless you work on Source files directly (similar to what APT does at compile time).
One simple method to read additional symbol informations from Java bytecode is:
Reflector reflector = new Reflector();
JavaMethod method = reflector.reflect(Whatever.class)
.getMethods()
.stream()
.filter(m -> "aMethod".equals(m.getName()))
.findFirst()
.get();
String paramName = method.getParameters().getVariables().get(0).getName();
System.out.println(paramName);
From Maven Central artifact:
<dependency>
<groupId>com.intersult</groupId>
<artifactId>coder</artifactId>
<version>1.5</version>
</dependency>

PowerMock and Mockito not working for TestNG

I have a utility class with a static method
public class A {
public static boolean test1(){
// Do something
return true;
}
}
I am trying to mock test1 using Powermockito and using TestNG for testing
#PrepareForTest(A.class)
public class UnitTest{
#Test
public void testTest1() {
PowerMockito.mockStatic(A.class);
when(A.test1()).thenReturn(false);
}
}
https://code.google.com/p/powermock/wiki/TestNG_usage
Describes me to do this way.
However, in "when(A.test1()).thenReturn(false);" it calls the actual method test1() during the Mockito.when setup for test1() method. Hence, I believe the setup is not done right where it cannot recognize Class A as a Mock
Am I doing something wrong here?
My dependencies in pom.xml -
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<scope>test</scope>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
After the comment from #Damien Beaufils, I tried to google more and finally found a post in the powermock issues; The google group describing the same problem.
The solution is that your test should extend PowerMockTestCase (which is imported from the testng powermock module i.e org.powermock.modules.testng)
More info - code.google.com/p/powermock/issues/detail?id=54#c9

Resources