How can I mock a call to Spring's repository `saveAll()` method using mockk? - kotlin

I am using Mockk as my mocking framework when testing my Spring Boot Data repository interfaces.
Actually I am doing the following
every { itemRepository.saveAll(listOf(any(), any())) } returns listOf<Item>(mockk())
which should mock the following behaviour
val loot: List<Item> = itemGenerator.generateLoot(lootTable)
itemRepository.saveAll(loot)
The error message I receive is the following:
Failed matching mocking signature for
SignedCall(retValue=, isRetValueMock=true, retType=class kotlin.collections.Iterable, self=ItemRepository(#28), method=saveAll(Iterable), args=[[com.barbarus.gameserver.item.Item#ea00de, com.barbarus.gameserver.item.Item#23ca36d]], invocationStr=ItemRepository(#28).saveAll([com.barbarus.gameserver.item.Item#ea00de, com.barbarus.gameserver.item.Item#23ca36d]))
left matchers: [any(), any()]
The error message says left matchers: [any(), any()] pointing out that I somehow am not defining the expected arguments right.
I could fully define the items by real implementations in my test logic but I'd like to stick with mockk() just to keep the test code slim and fast.
However I kinda am not able to define the List<Item> with two elements using listOf(any(),any()) here. I tried other API of Mockk without any luck.
Any idea what to use in this case?

You should type the any() when you are passing into saveAll().
For instance:
import com.barbarus.gameserver.item.Item
...
every { itemRepository.saveAll(any<List<Item>>() } returns listOf<Item>(mockk())
Solution from another post

Related

quarkus reactive getting started PUT and DELETE operations

I'm going through the quarkus reactive getting started page and PUT and DELETE method implementations are missing.
Seem like it assumes we already know quarkus and are just reading the guide to switch from non-reactive to reactive. Why they don't provide a full example, I don't know. I mean where would you learn if not by a guide or someone showing you how it's done?
PUT should replace an entry and DELETE should delete one.
PUT /{id} = replace
DELETE /{id} = delete
Instead of Fruit my entity is named Profile.
package de.icod.reso.resources;
import de.icod.reso.entities.Profile;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.panache.common.Sort;
import io.smallrye.mutiny.Uni;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.List;
import java.util.UUID;
#Path("/profile")
#ApplicationScoped
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class ProfileResource {
#GET
public Uni<List<Profile>> get() {
return Profile.listAll(Sort.by("name"));
}
#GET
#Path("/{id}")
public Uni<Profile> getSingle(UUID id) {
return Profile.findById(id);
}
#POST
public Uni<Response> create(Profile profile) {
return Panache.<Profile>withTransaction(profile::persist)
.onItem().transform(inserted -> Response.created(URI.create("/profile" + inserted.id)).build());
}
#PUT
#Path("/{id}")
public Uni<Response> replace(UUID id, Profile profile) {
// error: incompatible types: incompatible parameter types in method reference
return Panache.<Profile>withTransaction(profile::update)
.onItem().transform(updated -> Response.ok(URI.create("/profile" + updated.id)).build());
}
#DELETE
#Path("/{id}")
public Uni<Response> delete(UUID id) {
// delete entity by id
}
}
Can you fill the 2 missing functions?
I'm aware there is Quarkus Getting Started (w/ Reactive): PostGres/Docker CRUD Application -- Missing POST, PUT, and DELETE REST URLs
but the contents are different from what's written in the getting started page.
Generally speaking, the Getting Started and the quickstarts might be slightly different. The reason is that the purpose of the Getting Started is to help a new user to set up a small working project quickly and the quickstart is a place to showcase different functionalities of the extensions involved.
We try to make sure that the documentation is always up to date but nothing beats a working example like the quickstart.
In this case I don't understand your complains.
An example of PUT and DELETE is available in the Hibernate Reactive with panache quickstart:
#PUT
#Path("{id}")
public Uni<Response> update(Long id, Fruit fruit) {
return Panache
.withTransaction(() -> Fruit.<Fruit> findById(id)
.onItem().ifNotNull().invoke(entity -> entity.name = fruit.name)
)
.onItem().ifNotNull().transform(entity -> Response.ok(entity).build())
.onItem().ifNull().continueWith(Response.ok().status(NOT_FOUND)::build);
}
#DELETE
#Path("{id}")
public Uni<Response> delete(Long id) {
return Panache.withTransaction(() -> Fruit.deleteById(id))
.map(deleted -> deleted
? Response.ok().status(NO_CONTENT).build()
: Response.ok().status(NOT_FOUND).build());
}
For your use case you need to use Profile instead of Fruit, and UUID instead of Long for the id. I don't think you need anything else to make it work.
// error: incompatible types: incompatible parameter types in method reference
The error message is telling you the cause of the problem: the syntax Panache.withTransaction(profile::update) is not correct.
This won't work because profile.update(...) expects additional parameters that you are not passing when using method reference with withTransaction.
On the other hand, one can use Panache.withTransaction(profile::persist) because profile.persist() is a method that doesn't require parameters.
This is how method reference works in Java.
That said, the documentation is never done and it will get better over time based on the feedback we receive.
Also, StackOverflow might not be the best place for this type of questions.
I would have probably asked this on the users stream on Zulip first. It's even faster to receive an answer if you push an example of the code that's giving you trouble somewhere, so that we can run it and give you precise help about what's wrong with it.
I'm aware there is Quarkus Getting Started (w/ Reactive): PostGres/Docker CRUD Application -- Missing POST, PUT, and DELETE REST URLs
The example in this question is 2 years old and things have changed since then. In fact, the same quickstart and tutorial referenced in that question now match the code I've used.
You can find the information you are asking for at https://quarkus.io/guides/hibernate-orm-panache#writing-a-jax-rs-resource.
You can also compare reactive vs. non-reactive versions of the same application by looking at the Hibernate Reactive Panache Quickstart and the Hibernate ORM Panache Quickstart

The method expect(boolean) is undefined for the type in Shiro test

I'm doing my first test in Java, and I have a Shiro Security... I follow the tutorial (https://shiro.apache.org/testing.html) but says:
(this example uses EasyMock, but Mockito works equally as well):
Subject subjectUnderTest = createNiceMock(Subject.class);
expect(subjectUnderTest.isAuthenticated()).andReturn(true);
Because I use Mockito I implement with
Subject mockSubject = mock(Subject.class);
expect(subjectUnderTest.isAuthenticated()).andReturn(true);
But when I do it have this error
The method expect(boolean) is undefined for the type AdminControllerTest
And don't give me the posibility to import it. I don't know if expect is especific of EasyMock and if yes what I have to use in Mockito.
I search here and see more person doing it and always recomend use this expect
How to mock a shirosession?
If we look at this code example ...
Subject mockSubject = mock(Subject.class);
expect(subjectUnderTest.isAuthenticated()).andReturn(true);
We can see that ...
You are using mockito syntax to do the mocking.
You are using easyMock syntax to configure the mock. It is not even in the dependency list, so this method is not found.
The solution is to use mockito syntax to configure the mock.
Subject mockSubject = mock(Subject.class);
when(mockSubject.isAuthenticated()).thenReturn(true);
This will make everything work as expected and your Subject will return true, when the isAuthenticated() method is called.
If you want to up your mockito game, try this resource, which comes with working github code examples.

Is it possible to expect dispatch command from a non axon-class in AxonFramework

Suppose I have some classes which is not axon class (etc. Saga or agrgregate), and if I want it to dispatch command I could use command gateway, but here is the problem. I want to write
unit testing to make sure that this non axon-class has dispatched command already. So if it's either saga or aggregate I could use fixture and then given command or event, but is it possible to use fixture with these non axon-class also.
Here is how the code looks like
class MyService {
//...
lateinit var commandGateway: CommandGateway
fun doSomething(command: doSomethingCommand){
commandGateway.send(command)
}
}
class MyServiceTest {
//...
#Test
fun doSomething_ShouldDispatchDoSomethingCommand(){
// expect dispatch command from non axon-class
}
}
As you've noticed, Axon Framework provides Test Fixtures for Aggregates and Sagas, without specifying any additional options on the matter. The AggregateTestFixture does provide one method along the lines you are for, which is the AggregateTestFixture#registerAnnotatedCommandHandler. However, this is there to validate a component containing #CommandHandler annotated methods, not to validate dispatching.
Thus I think the most straightforward approach would be to mock or spy the CommandGateway/CommandBus to do the validation on to be honest.

Get pluginId stored in the IPreferenceNode in Eclipse

I am developing a plugin, in my plugin I want to get another plugin ID. I use the following code:
PreferenceManager pm = PlatformUI.getWorkbench( ).getPreferenceManager();
List<IPreferenceNode> list = pm.getElements(PreferenceManager.PRE_ORDER);
String pluginid;
// restoreDefValues("org.eclipse.ant.ui");
for(IPreferenceNode node : list){
the code to find the node related to the plugin;
}
When I debug the program, I can clearly see that in variable node(IPreferenceNode), it has the value of the pluginId. However, I check the document of IPreferenceNode, it seems that the neither IPreferenceNode nor the class PreferenceNode, provide a method to return the value of pluginId. I tried node.toString() as well, couldn't get the pluginId. So what should I do? Is there any other ways to get a plugin ID from another plugin?
Preference nodes created using the org.eclipse.ui.preferencePages extension point will actually be instances of org.eclipse.ui.internal.dialogs.WorkbenchPreferenceNode. The super class of this (WorkbenchPreferenceExtensionNode) contains the plugin id.
These classes are internal so you should not try to use them directly. However they implement org.eclipse.ui.IPluginContribution which can be used and has a getPluginId() method.
So something like:
if (node instanceof IPluginContribution) {
pluginId = ((IPluginContribution)node).getPluginId();
}
should work.

JAX-RS return a Map<String,String>

I want to retrieve a Map from a using JAX-RS (text/xml)
#GET
public Map<String,String> getMap(){
}
but I am getting the error below:
0000001e FlushResultHa E org.apache.wink.server.internal.handlers.FlushResultHandler handleResponse The system could not find a javax.ws.rs.ext.MessageBodyWriter or a DataSourceProvider class for the java.util.HashMap type and application/x-ms-application mediaType. Ensure that a javax.ws.rs.ext.MessageBodyWriter exists in the JAX-RS application for the type and media type specified.
[10:43:52:885 IST 07/02/12] 0000001e RequestProces I org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (500 - Internal Server Error) with message 'null' while processing GET request sent to http://localhost:9080/jaxrs_module/echo/upload/getSiteNames
The solution I choose is to wrap a Map and use it for the return param.
#XmlRootElement
public class JaxrsMapWrapper {
private Map<String,String> map;
public JaxrsMapWrapper(){
}
public void setMap(Map<String,String> map) {
this.map = map;
}
public Map<String,String> getMap() {
return map;
}
}
and the method signature will go like this
#GET
public JaxrsMapWrapper getMap()
Your problem is that the default serialization strategy (use JAXB) means that you can't serialize that map directly. There are two main ways to deal with this.
Write an XmlAdaptor
There are a number of questions on this on SO but the nicest explanation I've seen so far is on the CXF users mailing list from a few years ago. The one tricky bit (since you don't want an extra wrapper element) is that once you've got yourself a type adaptor, you've got to install it using a package-level annotation (on the right package, which might take some effort to figure out). Those are relatively exotic.
Write a custom MessageBodyWriter
It might well be easier to write your own code to do the serialization. To do this, you implement javax.ws.rs.ext.MessageBodyWriter and tag it with #Provider (assuming that you are using an engine that uses that to manage registration; not all do for complex reasons that don't matter too much here). This will let you produce exactly the document you want from any arbitrary type at a cost of more complexity when writing (but at least you won't be having complex JAXB problems). There are many ways to actually generate XML, with which ones to choose between depending on the data to be serialized
Note that if you were streaming the data out rather than assembling everything in memory, you'd have to implement this interface.
Using CXF 2.4.2, it supports returning Map from the api. I use jackson-jaxrs 1.9.6 for serialization.
#Path("participation")
#Consumes({"application/json"})
#Produces({"application/json"})
public interface SurveyParticipationApi {
#GET
#Path("appParameters")
Map<String,String> getAppParameters();
....
}
With CXF 2.7.x use
WebClient.postCollection(Object collection, Class<T> memberClass, Class<T> responseClass)
,like this in your rest client code.
(Map<String, Region>) client.postCollection(regionCodes, String.class,Map.class);
for other collections use WebClient.postAndGetCollection().