-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EDGFQM-8 Add controller pass-through test #9
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package org.folio.fqm.edge.controller; | ||
|
||
import com.flextrade.jfixture.JFixture; | ||
import org.folio.fqm.edge.client.EntityTypesClient; | ||
import org.folio.fqm.edge.client.QueryClient; | ||
import org.folio.fqm.edge.service.EntityTypesService; | ||
import org.folio.fqm.edge.service.QueryService; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.mock.mockito.MockBean; | ||
import org.springframework.boot.test.mock.mockito.SpyBean; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.util.Set; | ||
import java.util.stream.Stream; | ||
|
||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
|
||
@SpringBootTest | ||
class EndpointPassThroughTest { | ||
|
||
@Autowired | ||
private EntityTypesController entityTypesController; | ||
@SpyBean | ||
private EntityTypesService entityTypesService; | ||
@MockBean | ||
private EntityTypesClient entityTypesClient; | ||
|
||
@Autowired | ||
private QueryController queryController; | ||
@SpyBean | ||
private QueryService queryService; | ||
@MockBean | ||
private QueryClient queryClient; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
} | ||
|
||
@Test | ||
void testEntityTypesPassThrough() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { | ||
// Skip getRequests() in the entity type controller, since it's only there to resolve a name conflict in its | ||
// two parent interfaces | ||
verifyPassThrough(entityTypesController, entityTypesService, entityTypesClient, "getRequest"); | ||
} | ||
|
||
@Test | ||
void testQueryPassThrough() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { | ||
verifyPassThrough(queryController, queryService, queryClient); | ||
} | ||
|
||
/** | ||
* Verify that all methods in controller pass straight through to the service and client. | ||
* <p> | ||
* Note: There's a build-in assumption here that the controller, service, and client have the same basic interface; as | ||
* in, if a method exists in the controller, then both the corresponding service and client should have methods with the | ||
* same signature. | ||
* | ||
* @param controller - The controller to verify | ||
* @param service - The backing service for the controller | ||
* @param client - The underlying client that the service uses to retrieve data | ||
* @param methodsToIgnore - Method names to skip | ||
*/ | ||
private <C, S, R> void verifyPassThrough(C controller, S service, R client, String... methodsToIgnore) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { | ||
var ignoredMethods = Set.of(methodsToIgnore); | ||
for (var controllerMethod : controller.getClass().getMethods()) { | ||
// Skip any non - overridden inherited methods, along with any explicitly skipped ones | ||
if (!controllerMethod.getDeclaringClass().equals(controller.getClass()) || ignoredMethods.contains(controllerMethod.getName())) { | ||
continue; | ||
} | ||
// Given a controller, service, and client method | ||
var serviceMethod = service.getClass().getMethod(controllerMethod.getName(), controllerMethod.getParameterTypes()); | ||
var clientMethod = client.getClass().getMethod(controllerMethod.getName(), controllerMethod.getParameterTypes()); | ||
|
||
// And given a set of random method arguments for the controller method | ||
JFixture fixture = new JFixture(); | ||
var args = Stream.of(controllerMethod.getParameterTypes()).map(fixture::create).toArray(); | ||
|
||
// When we call the controller method with those arguments | ||
controllerMethod.invoke(controller, args); | ||
|
||
// Then the service and client methods should get called with the same arguments exactly once | ||
serviceMethod.invoke(verify(service, times(1)), args); | ||
clientMethod.invoke(verify(client, times(1)), args); | ||
Comment on lines
+86
to
+87
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These took me a while to figure out, since the normal There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking the same thing when looking at it! |
||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I usually really dislike JFixture (I usually prefer something like junit-quickcheck instead for randomized testing), but for something like this, where it's not truly randomized testing and we just need some non-null objects of the right types (non-null, so that we can verify that they can be differentiated), it's pretty sweet