I'm having difficulties trying to mock a dependency within Guava Collection.
Let's assume I have a following code to test:
#Service
public final class ServiceA {
private final ServiceB serviceB;
#Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
public Collection<String> runAll(Collection<String> dataList) {
final ImmutableList.Builder<String> builder = ImmutableList.builder();
for (String data : dataList) {
builder.add(serviceB.run(data));
}
return builder.build();
}
}
My Spock Spec looks like this:
class ServiceASpec extends Specification {
def serviceB = Mock(ServiceB.class)
def serviceA = new ServiceA(serviceB)
def "test"() {
when:
def strings = serviceA.runAll(['data1', 'data2'])
then:
1 * serviceB.run('data1') >> 'result1'
1 * serviceB.run('data2') >> 'result2'
0 * _._
strings == ['result1', 'result2']
}
}
This spec runs just fine and it is doing what I want it to do.
Then I refactored my implementation to use Guava's Collections2.transform(..):-
#Service
public final class ServiceA {
private final ServiceB serviceB;
#Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
public Collection<String> runAll(Collection<String> dataList) {
return Collections2.transform(dataList, new Function<String, String>() {
#Override
public String apply(final String input) {
return serviceB.run(input);
}
});
}
}
When I rerun my spec, I'm getting this error:-
Too few invocations for:
1 * serviceB.run('data1') >> 'result1' (0 invocations)
Unmatched invocations (ordered by similarity):
None
Too few invocations for:
1 * serviceB.run('data2') >> 'result2' (0 invocations)
Unmatched invocations (ordered by similarity):
None
My take is it has something to do with the mocking timing because the Guava function will only be executed when the collection is used.
However, I wasn't sure how to refactor my spec to make this to work.
How do I go about solving this? Thanks.
Under the hood transform() method returns TransformedCollection class. As you can see here transformation is applied no sooner than on iterating the wrapped collection. Since you don't iterate the transformed collection mocked service is not invoked and no interaction is recorded.
It seems that simply iterating the collection should solve the problem, however such test should be really well documented.
Another way is using FluentIterable.from(list).transform(function).toList() instead of Collections2.transform(list, function).
Related
I am setting up a Spring Boot application (DAO pattern with #Repositories) where I am attempting to write a #Service to asynchronously pull data from a database in multiple threads and merge-process the incoming payloads sequentially, preferably on arrival.
The goal is to utilize parallel database access for requests where multiple non-overlapping sets of filter conditions need to be queried individually, but post-processed (transformed, e.g. aggregated) into a combined result.
Being rather new to Java, and coming from Golang and its comparably trivial syntax for multi-threading and task-communication, I struggle to identify a preferable API in Java and Spring Boot - or determine if this approach is even favorable to begin with.
Question:
Given
a Controller:
#RestController
#RequestMapping("/api")
public class MyController {
private final MyService myService;
#Autowired
public MyController(MyService myService) {
this.myService = myService;
}
#PostMapping("/processing")
public DeferredResult<MyResult> myHandler(#RequestBody MyRequest myRequest) {
DeferredResult<MyResult> myDeferredResult = new DeferredResult<>();
myService.myProcessing(myRequest, myDeferredResult);
return myDeferredResult;
}
a Service:
import com.acme.parallel.util.MyDataTransformer
#Service
public class MyServiceImpl implementing MyService {
private final MyRepository myRepository;
#Autowired
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
public void myProcessing(MyRequest myRequest, MyDeferredResult myDeferredResult) {
MyDataTransformer myDataTransformer = new MyDataTransformer();
/* PLACEHOLDER CODE
for (MyFilter myFilter : myRequest.getMyFilterList()) {
// MyPartialResult myPartialResult = myRepository.myAsyncQuery(myFilter);
// myDataTransformer.transformMyPartialResult(myPartialResult);
}
*/
myDeferredResult.setResult(myDataTransformer.getMyResult());
}
}
a Repository:
#Repository
public class MyRepository {
public MyPartialResult myAsyncQuery(MyFilter myFilter) {
// for the sake of an example
return new MyPartialResult(myFilter, TakesSomeAmountOfTimeToQUery.TRUE);
}
}
as well as a MyDataTransformer helper class:
public class MyDataTransformer {
private final MyResult myResult = new MyResult(); // e.g. a Map
public void transformMyPartialResult(MyPartialResult myPartialResult) {
/* PLACEHOLDER CODE
this.myResult.transformAndMergeIntoMe(myPartialResult);
*/
}
}
how can I implement
the MyService.myProcessing method asynchronously and multi-threaded, and
the MyDataTransformer.transformMyPartialResult method sequential/thread-safe
(or redesign the above)
most performantly, to merge incoming MyPartialResult into one single MyResult?
Attempts:
The easiest solution seems to be to skip the "on arrival" part, and a commonly preferred implementation might e.g. be:
public void myProcessing(MyRequest myRequest, MyDeferredResult myDeferredResult) {
MyDataTransformer myDataTransformer = new MyDataTransformer();
List<CompletableFuture<myPartialResult>> myPartialResultFutures = new ArrayList<>();
for (MyFilter myFilter : myRequest.getMyFilterList()) { // Stream is the way they say, but I like for
myPartialResultFutures.add(CompletableFuture.supplyAsync(() -> myRepository.myAsyncQuery(myFilter));
}
myPartialResultFutures.stream()
.map(CompletableFuture::join)
.map(myDataTransformer::transformMyPartialResult);
myDeferredResult.setResult(myDataTransformer.getMyResult());
}
However, if feasible I'd like to benefit from sequentially processing incoming payloads when they arrive, so I am currently experimenting with something like this:
public void myProcessing(MyRequest myRequest, MyDeferredResult myDeferredResult) {
MyDataTransformer myDataTransformer = new MyDataTransformer();
List<CompletableFuture<myPartialResult>> myPartialResultFutures = new ArrayList<>();
for (MyFilter myFilter : myRequest.getMyFilterList()) {
myPartialResultFutures.add(CompletableFuture.supplyAsync(() -> myRepository.myAsyncQuery(myFilter).thenAccept(myDataTransformer::transformMyPartialResult));
}
myPartialResultFutures.forEach(CompletableFuture::join);
myDeferredResult.setResult(myDataTransformer.getMyResult());
}
but I don't understand if I need to implement any thread-safety protocols when calling myDataTransformer.transformMyPartialResult, and how - or if this even makes sense, performance-wise.
Update:
Based on the assumption that
myRepository.myAsyncQuery takes slightly varying amounts of time, and
myDataTransformer.transformMyPartialResult taking an ever increasing amount of time each call
implementing a thread-safe/atomic type/Object, e.g. a ConcurrentHashMap:
public class MyDataTransformer {
private final ConcurrentMap<K, V> myResult = new ConcurrentHashMap<K, V>();
public void transformMyPartialResult(MyPartialResult myPartialResult) {
myPartialResult.myRows.stream()
.map((row) -> this.myResult.merge(row[0], row[1], Integer::sum)));
}
}
into the latter Attempt (processing "on arrival"):
public void myProcessing(MyRequest myRequest, MyDeferredResult myDeferredResult) {
MyDataTransformer myDataTransformer = new MyDataTransformer();
List<CompletableFuture<myPartialResult>> myPartialResultFutures = new ArrayList<>();
for (MyFilter myFilter : myRequest.getMyFilterList()) {
myPartialResultFutures.add(CompletableFuture.supplyAsync(() -> myRepository.myAsyncQuery(myFilter).thenAccept(myDataTransformer::transformMyPartialResult));
}
myPartialResultFutures.forEach(CompletableFuture::join);
myDeferredResult.setResult(myDataTransformer.getMyResult());
}
is up to one order of magnitude faster than waiting on all threads first, even with atomicity protocol overhead.
Now, this may have been obvious (not ultimately, though, as async/multi-threaded processing is by far not always the better choice), and I am glad this approach is a valid choice.
What remains is what looks to me like a hacky, flexibility lacking solution - or at least an ugly one. Is there a better approach?
I have a class for which i am writing groovy test case. This class has constructor autowiring and calls another method to initialise the fields.:
#Service
public class ServiceA {
private final PrincipalDao principalDao;
#Autowired
public ServiceA(final PrincipalDao principalDao){
this.principalDao=principalDao;
this.serviceMap = getMap();
}
private Map<> getMap() {
final List<ClassA> classAList = principalDao.findAll(); //this line returns null
}
}
this line final List<ClassA> classAList = principalDao.findAll(); returns null and I cant mock it in groovy like following:
principalDao.findAll() >> list
because its called even before my above line is called in groovy test case
Without seeing you unit test, it's hard to tell what the issue is, but a common misconception I run into sometimes is the timing of the mock.
Here's a sample code elaborating what I mean. Since parts of the code missing from your question, going to assume some parts of it, but I'm sure it explains the point I'm making and hopefully should help you out in finding an answer:
Service class:
#Service
public class ServiceA{
private final PrincipalDao principalDao;
private final Map<String, String> serviceMap;
#Autowired
public ServiceA(final PrincipalDao principalDao){
this.principalDao = principalDao;
this.serviceMap = getMap();
}
private Map<String, String> getMap(){
final HashMap<String, String> stringStringHashMap = new HashMap<String, String>();
stringStringHashMap.put("hello", principalDao.khello());
return stringStringHashMap;
}
public String printServiceMap() {
return serviceMap.get("hello");
}
}
class ServiceATest extends Specification {
def "some test"() {
given:
PrincipalDao principalDao = Mock()
principalDao.khello() >> "khello" // this is the key here. Mocking is happening before instantiation of ServiceA
ServiceA someService = new ServiceA(principalDao)
expect:
"khello" == someService.printServiceMap()
}
}
This unit test passes successfully. The point here is the timing of the mocking. principalDao.khello() >> "khello" happens before new ServiceA(principalDao).
If you don't need to modify serviceMap per unit test, you can also do this so you don't have to define it per unit test:
class ServiceATest extends Specification {
#Shared
PrincipalDao principalDao = Mock()
ServiceA someService = new ServiceA(principalDao)
def setupSpec() {
principalDao.khello() >> "khello"
}
def "some test"() {
expect:
"khello" == someService.printServiceMap()
}
}
I have created a factory to provide instance of IMyProcessor based on some boolean flag.
The below populates the map with both of my implementations.
#Component
public class MyProcessorFactory {
private static final Map<String, IMyProcessor> processorServiceCache = new HashMap<>();
#Value("${processor.async:true}")
private boolean isAsync;
public MyProcessorFactory(final List<IMyProcessor> processors) {
for (IMyProcessor service : processors) {
processorServiceCache.put(service.getType(), service);
}
}
public IMyProcessor getInstance() {
IMyProcessor processor = isAsync ? processorServiceCache.get("asynchronous") : processorServiceCache.get("synchronous");
return processor;
}
}
I am now trying to write a Unit test using Junit5 but I am struggling to setup the List of implementations:
I have tried the following:
#ExtendWith(MockitoExtension.class)
class ProcessorFactoryTest {
#InjectMocks
private MyProcessorFactory myProcessorFactory;
#Test
void testAsyncIsReturned() {
}
#Test
void testSyncisReturned() {}
}
I want to test based on the boolean flag async true/false, the correct implementation is returned.
It will be helpful to see how you write such test cases. I autowire the implementations of the interface as construction injection into a list then add to a map using a string key.
Along with answer, I am open to other ideas/refactorings that may make the testing easier.
I try to mock same method calls with different collection-arguments.
My problem is that im not getting the correct mocked-answer from Mocked-Call for the input.
Test-Class:
#ExtendWith(SpringExtension.class)
public class CollectionTest {
#MockBean
private Controller c;
#BeforeEach
public void init() {
Collection<String> a = Mockito.anyCollection();
a.add("a");
Mockito.when(c.run(a)).thenReturn("a");
Collection<String> b = Mockito.anyCollection();
b.add("b");
Mockito.when(c.run(b)).thenReturn("b");
}
#Test
public void test() {
assertEquals("a", c.run(Lists.newArrayList("a"))); // DOESNT'WORK!!! Returns "b" but should "a"
assertEquals("b", c.run(Lists.newArrayList("b"))); //
}
}
Controller-Class:
#Service
public class Controller{
public String run(Collection<String> c) {
return "not-mocked";
}
}
I'v got no idea why it doesn't return "a". I tried to change the collection to string but same behaviour.
What are the Steps to do, to get the following behaviour?
#Test
public void test() {
assertEquals("a", c.run(Lists.newArrayList("a"))); // should return "a"
assertEquals("b", c.run(Lists.newArrayList("b"))); // should return "b"
}
Im using Java Mockito "3.1" and Spring, but I think Mockito is the important information here.
Your second call - Mockito.when(c.run(b)).thenReturn("b");
is overruling our first call so Mockito will therefore always return "b".
If you need multiple answers from the same call, you can use the varags variant:
when(c.run(anyCollection())).thenReturn("a", "b");
Now the first call to the controller's run method will return "a" and all subsequent calls will return "b". You can provide as many return results as you want and the last one will be repeated from then on as the answer.
Write two tests will show you the results you are expecting.
You are adding to the same Controller two different results so you get only the last one : Mockito.when(c.run(b)).thenReturn("b");
Normal. The last mocked expected result in your setUp() will stay in memory.
Previous answer was :
You can use something like junit and mockito to test your spring-web-mvc application.
It looks like that :
#WebMvcTest(controllers = UserController.class)
#ActiveProfiles("test")
class UserControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private UserService userService;
private List<User> userList;
#BeforeEach
void setUp() {
this.userList = new ArrayList<>();
this.userList.add(new User(1L, "user1#gmail.com", "pwd1","User1"));
this.userList.add(new User(2L, "user2#gmail.com", "pwd2","User2"));
this.userList.add(new User(3L, "user3#gmail.com", "pwd3","User3"));
}
}
And as an example :
#Test
void shouldFetchAllUsers() throws Exception {
given(userService.findAllUsers()).willReturn(userList);
this.mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()", is(userList.size() )));
}
Example from #see https://medium.com/backend-habit/integrate-junit-and-mockito-unit-testing-for-controller-layer-91bb4099c2a5
I have a test in which I want to verify that a method was called with given parameters:
#Autowired
private Client client;
#Autowired
private OtherClient otherClient;
#Test
public void test() {
client.push();
Mockito.verify(otherClient).publishReset(
Mockito.anyString(),
Mockito.argThat(l -> l.size() == 3)
);
}
Problem is that Mockito.verify doesn't fail at all, I can replace l -> l.size() == 3 with any other size match and given test will always pass. How is it even possible for verify to always pass whatever I pass to argThat?
Full example below:
#EnableConfigurationProperties
#TestExecutionListeners(listeners = {
DirtiesContextTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class,
ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
MockitoTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExecutionListener.class
})
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ContextConfiguration(
loader = SpringBootContextLoader.class,
classes = {MyApp.class, IntegrationTestContext.class})
#RunWith(SpringRunner.class)
public class FooIT {
#Autowired
private Client client;
#Autowired
private OtherClient otherClient;
#Test
public void test() {
client.push();
Mockito.verify(otherClient).publishReset(
Mockito.anyString(),
Mockito.argThat(l -> l.size() == 3)
);
}
}
And a configuration class:
#Configuration
#MockBeans({
#MockBean(OtherClient.class),
})
public class IntegrationTestContext {
}
Is there something that I'm doing wrong? Is Spring interfering with mockito somehow?
Mockito has problems spying on Spring proxies and final classes/methods. In some cases, this may help:
Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable));
//
// In your case:
#Test
public void test() {
OtherClient yourSpy = Mockito.mock(OtherClient.class,
AdditionalAnswers.delegatesTo(otherClient));
client.push();
Mockito.verify(yourSpy).publishReset(
Mockito.anyString(),
Mockito.argThat(l -> l.size() == 3)
);
}
This has helped me with a similar problem and was inspired by this Mockito-issue on Github.
Problem was in garbage collector, I created a large list l that container a lot of objects (> 30k with many fields), when I reduced the size to e.g. 100 everything started to work correctly.
So basically: don't force mockito to work with objects that take too much memory (probably some kind of weak reference is used), e.g. reduce the size of lists.