How to mock ValueMap in Spock framework in CQ5? - aem

I am writing unit test cases using Spock framework for CQ5 project.
def props1 = Mock(ValueMap)
props1.put("prop1",21);
props1.put("prop2",92);
I am not able to add properties to props1.

I am not familiar with Spock but in Java tests you do not need to mock a ValueMap. Fortunately, there is a better way to create a ValueMap using org.apache.sling.api.wrappers.ValueMapDecorator:
final Map<String, Object> properties = new HashMap<>();
properties.put("prop1", 21);
properties.put("prop2", 92);
final ValueMap valueMap = new ValueMapDecorator(properties);
Maybe you can adapt that for Spock.
Documentation:
https://helpx.adobe.com/experience-manager/6-3/sites/developing/using/reference-materials/javadoc/org/apache/sling/api/wrappers/ValueMapDecorator.html
https://helpx.adobe.com/experience-manager/6-3/sites/developing/using/reference-materials/javadoc/org/apache/sling/api/wrappers/ModifiableValueMapDecorator.html

Well, I just took a break and needed something else to think about, so I made up a quick example for you. But before we look into the code, the explanation why you cannot call put(..) on a Mock(ValueMap): simply because the interface ValueMap only has get(..) methods. So you have two ways of testing with a ValueMap:
Use a real object as suggested by Jens. How to use the ValueMapDecorator suggested by him can be seen in my first feature method.
Use a Mock(ValueMap) or, even better, just a Stub(ValueMap) and specify (stub) its behaviour when calling get(..) methods with values used during the test. This way you do not need to populate the real object with values and use a concrete class, but just tell the stubbed interface how to behave.
package de.scrum_master.stackoverflow
import org.apache.sling.api.resource.ValueMap
import org.apache.sling.api.wrappers.ValueMapDecorator
import spock.lang.Specification
class SlingValueMapTest extends Specification {
def "Real ValueMap"() {
given:
Map<String, Object> map = new HashMap<>()
map.put("prop1", 21)
map.put("prop2", 92)
ValueMap valueMap = new ValueMapDecorator(map)
expect:
valueMap.get("prop1") == 21
valueMap.get("foo") == null
valueMap.get("prop2") == 92
valueMap.get("bar") == null
}
def "Stubbed ValueMap"() {
given:
ValueMap valueMap = Stub() {
get("prop1") >> 21
get("prop2") >> 92
get(_) >> null
}
expect:
valueMap.get("prop1") == 21
valueMap.get("foo") == null
valueMap.get("prop2") == 92
valueMap.get("bar") == null
}
}

Related

mapstruct null check causes spotbug error

We have a mapstruct mapper defined for a source and a target that also use lombok.
#Value
class Source {
#NonNull List<ElementSource> elements;
}
#Value
class Target {
#NonNull List<ElementTarget> elements;
}
#Mapper
public interface Mapper {
Target mapToTarget(Source source);
}
mapstruct generated mapper implementation looks like below code.
List<ElementTarget> elements = null;
List<Element> list = source.getColumns();
if ( list != null ) {
elements = new ArrayList<ElementTarget>( list );
}
Target target = new Target( elements );
return target;
Now this piece of mapper code raises spotbug error with type NP_NULL_PARAM_DEREF: Method call passes null for non-null parameter. Because it detects elements can be null when the code creates Target object.
Now what is the best option to handle this.
Suppress spotbug to ignore this error type for mapstruct generated mapper classes. But the mapper class still has a code smell here.
Define a customzed mapper method for elements itself and throw exception if source object return null list.
if there is a way to configure mapstruct to not generate null check for elements at all.
if there is a way to configure mapstruct to throw exception when null check of elements return true.
For option 3 & 4, I can't find an answer whether mapstruct support it or not.
The ideal solution would be option 3. This is currently not supported by MapStruct. However, it is a requested feature in mapstruct/mapstruct#1243. I would suggest voting for it if you are interested in it.
Option 4 would also be a new feature, that I am not sure how much it belongs in MapStruct, but you can try raising it as a feature request to get the feedback from the community.

Is using default method in an interface a good pattern to avoid code duplications?

We have a lot of code duplication in data holder classes that can be serialized to a XML string:
public String toXml() throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(this.getClass());
final Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
final StringWriter stringWriter = new StringWriter();
marshaller.marshal(this, stringWriter);
return stringWriter.toString();
}
Why not move this code to a single interface with default implementation? So a simple implements ToXmlUtf8 would be enough to share the default implementation and avoid code duplicates:
public interface ToXml {
default String toXml() throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(this.getClass());
final Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
final StringWriter stringWriter = new StringWriter();
marshaller.marshal(this, stringWriter);
return stringWriter.toString();
}
}
Has anybody done this before successfully?
Other solutions?
I could also imagine using an annotation to generate this code.
Are there any ready to use solutions available?
Yes, default methods can be used in that way.
Although the intended use case of default methods is adding new functionality to existing interfaces without breaking old code, default methods have other uses as well. Default methods are also used in interfaces which were added in Java 8, such as in java.util.Predicate, so even the Java designers recognized that adding new functionality to existing interfaces is not the only valid use of default methods.
A disadvantage could be that the implemented interfaces are part of a class's public contract, but in your case this does not seem to be a problem.
If you're using the exact same method then an interface won't help, what you want to do is make a static method and put that in a util class

scala: Moking my scala Object that has external dependency

I have a Object like this:
// I want to test this Object
object MyObject {
protected val retryHandler: HttpRequestRetryHandler = new HttpRequestRetryHandler {
def retryRequest(exception: IOException, executionCount: Int, context: HttpContext): Boolean = {
true // implementation
}
}
private val connectionManager: PoolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager
val httpClient: CloseableHttpClient = HttpClients.custom
.setConnectionManager(connectionManager)
.setRetryHandler(retryHandler)
.build
def methodPost = {
//create new context and new Post instance
val post = new HttpPost("url")
val res = httpClient.execute(post, HttpClientContext.create)
// check response code and then take action based on response code
}
def methodPut = {
// same as methodPost except use HttpPut instead HttpPost
}
}
I want to test this object by mocking dependent objects like httpClient. How to achieve this? can i do it using Mokito or any better way? If yes. How? Is there a better design for this class?
Your problem is: you created hard-to test code. You can turn here to watch some videos to understand why that is.
The short answer: directly calling new in your production code always makes testing harder. You could be using Mockito spies (see here on how that works).
But: the better answer would be to rework your production code; for example to use dependency injection. Meaning: instead of creating the objects your class needs itself (by using new) ... your class receives those objects from somewhere.
The typical (java) approach would be something like:
public MyClass() { this ( new SomethingINeed() ); }
MyClass(SomethingINeed incoming) { this.somethign = incoming; }
In other words: the normal usage path still calls new directly; but for unit testing you provide an alternative constructor that you can use to inject the thing(s) your class under test depends on.

Xtext: JvmModelInferrer initialize field

I'd like to generate a List field into my class generated from my DSL and initialize it like this:
private List<MyObject> myObjects= Lists.newArrayList();
The only way I know for this, is to append some text to the initializer:
members += appRule.toField("myObjects", appRule.newTypeRef(List, it.newTypeRef(MyObject))) [
initializer = [append('''Lists.newArrayList()''')]
]
However, using this approach the JvmModelInferrer won't import the Guava Strings library, thus will raise compilation issues. Is there any way to overcome this obstacle?
If I understand your issue (as you are referring to the Guava Strings library that is not used in the code :) ), your problem is, that the class reference Lists is not imported.
For such constructs, we have a helper method in EMF-IncQuery that serializes a type reference the same way parameters are serialized. This functionality relies on the injectable TypeReferenceSerializer class.
def referClass(ITreeAppendable appendable, EObject ctx, Class<?> clazz, JvmTypeReference... typeArgs) {
val ref = ctx.newTypeRef(clazz, typeArgs)
if (ref != null) {
appendable.serialize(ref, ctx)
} else {
//Class resolution error - error handling required here
//A fallback to writing out the fqn of the class
appendable.append(clazz.canonicalName)
}
}
def serialize(ITreeAppendable appendable, JvmTypeReference ref, EObject ctx) {
typeReferenceSerializer.serialize(ref, ctx, appendable)
}

JAXB How to force xsi:type in array of subclasses? (JBoss 4.2.3)

(Please note; i'm completely new to webservices, so the following may be stupid/incorrect but please be patient)
In my WebServices' #WebMethod I'm returning an array of an abstract base class (JAXB annotated entities in JBoss 4.2.3).
Obviously unless the type information is encoded per array element this will fail...
So how do I ensure that JAXB adds the xsi:type attribute?
My WebService interface has the following annotation, and I've tried every legal combination:
#SOAPBinding(style = RPC, parameterStyle = WRAPPED, use = LITERAL)
the methods on this interface take x2 parameters annotated #WebParam(name="...", mode=IN)
Other methods with similar signatures that don't return a heterogeneous array work perfectly.
Some related things:
Looks like JBoss uses the types defined in the method signatures to decide what classes to load into the JAXBContext - if I change the return types to Object[] it throws an error stating that the AbstractBase class "nor any of its super class is known to this context." I've added dummy methods returning the specific subclasses so that the generated WSDL has a list of all of them.
when I try to write tests for this, all is ok for single elements, but JAXB throws an error for array types: unable to marshal type "[LAbstractBase;" as an element because it is missing an #XmlRootElement annotation
From code like that shown below (note: AbstractBase, ConcreteOne and ConcreteTwo all have #XmlRootElement annotations)
private static final Class<?>[] CLASSES_TO_BE_BOUND = new Class<?>[]{
//Note; adding AbstractBase[].class doesn't work either
AbstractBase.class, ConcreteOne.class, ConcreteTwo.class
};
#Test
public void testXsiTypeAttributeIsIncludedInHeterogeneousArray()
{
AbstractBase[] array = new AbstractBase[2];
array[0] = new ConcreteOne();
array[1] = new ConcreteTwo();
Marshaller marshaller = createMarshaller();
StringWriter sw = new StringWriter();
marshaller.marshal(array, sw);
String output = sw.toString();
Assert.assertTrue(output.contains("xsi:type=\""));
}
private Marshaller createMarshaller() throws Exception {
JAXBContext context = JAXBContext.newInstance(CLASSES_TO_BE_BOUND);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
return marshaller;
}
Ideally I'd like to be able to test some bits relating to arrays, but it's far more critical that I can get the type information encoded per element in the JBoss environment.
Edit: Own Answer
JBoss (4.2.3) is doing something clever, but not too clever - it will handle the returning of arrays but not polymorphic arrays. This threw me a bit as I tried to get this way of doing it working in my tests.
Instead of trying to solve the JBoss WebService issue I made my tests more comprehensive - making the array a member of a simple container class and then annotating the array's getter with:
#XmlElementRefs({
#XmlElementRef(type = ConcreteOne.class),
#XmlElementRef(type = ConcreteTwo.class)
})
public AbstractBase[] getItems() { /*...*/ }
Which worked, and returning this in the JBoss WebService also worked! Though I'm using:
#SOAPBinding(style = DOCUMENT, parameterStyle = BARE, use = LITERAL)
So it's not adding the xsi:type attribute, but document nodes are correctly tagged:
<ConcreteOne>...</ConcreteOne>
At some point I'll change the WebService to use RPC as I don't really like the single argument restriction, but for now this is working fine.