-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial support for jpos injections in tests.
This first commit adds support for injecting a log source, and a mocked mux for jpos based application junit tests.
- Loading branch information
Andrés Alcarraz
committed
Mar 15, 2022
1 parent
664c69e
commit 75192bf
Showing
10 changed files
with
555 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# Module for aiding in unit tests | ||
|
||
This module provides annotations to inject some mock or frequently needed objects during testing. | ||
|
||
For example, it provides an injection for a `Log` object, and a `MUX` mock. | ||
|
||
## Log injection example | ||
In the following example, if the logger does not already exist, a default one, that logs to standard output is created with the given `logger` name, and assinged to the `Log` instance. | ||
```java | ||
@ExtendWith(LogSupplierExtension.class) | ||
class LogTest { | ||
@LogSource (logger="Q2", realm="log-test") | ||
Log log; | ||
|
||
@Test | ||
public void testDebug(){ | ||
log.debug("debug called"); | ||
} | ||
``` | ||
|
||
|
||
## Mux mocking injection example | ||
|
||
This test class is actually executed in this module's test. | ||
|
||
```java | ||
package org.jpos.ee.test; | ||
|
||
import org.jpos.iso.ISOException; | ||
import org.jpos.iso.ISOMsg; | ||
import org.jpos.iso.MUX; | ||
import org.jpos.q2.iso.QMUX; | ||
import org.jpos.util.NameRegistrar; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
|
||
import static org.junit.jupiter.api.Assertions.*; | ||
import static org.mockito.ArgumentMatchers.anyLong; | ||
import static org.mockito.ArgumentMatchers.same; | ||
import static org.mockito.Mockito.when; | ||
|
||
@ExtendWith(MUXSupplierExtension.class) | ||
class MUXSupplierExtensionTest { | ||
|
||
private static final String MUX_NAME = "connected-mux"; | ||
@MUXMock(connected = true, name = MUX_NAME) | ||
MUX connectedMux; | ||
|
||
@MUXMock(connected = false) | ||
MUX disconnectedMux; | ||
|
||
@Test | ||
void testConnectedMux() throws NameRegistrar.NotFoundException { | ||
assertSame(connectedMux, QMUX.getMUX(MUX_NAME)); | ||
assertTrue(connectedMux.isConnected()); | ||
} | ||
|
||
@Test | ||
void testDisconnectedMux() { | ||
assertFalse(disconnectedMux.isConnected()); | ||
} | ||
|
||
@Test | ||
void testMockRequest() throws NameRegistrar.NotFoundException, ISOException { | ||
ISOMsg request = new ISOMsg("2100"); | ||
ISOMsg response = new ISOMsg("2110"); | ||
when(connectedMux.request(same(request), anyLong())).thenReturn(response); | ||
MUX mux = QMUX.getMUX(MUX_NAME); | ||
assertSame(connectedMux, mux); | ||
assertTrue(mux.isConnected()); | ||
assertSame(response, mux.request(request, 1000L)); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
description = 'jPOS-EE :: Testing Module' | ||
|
||
dependencies { | ||
implementation libraries.jupiter_api | ||
implementation libraries.mockito | ||
implementation libraries.jpos | ||
} |
37 changes: 37 additions & 0 deletions
37
modules/test/src/main/java/org/jpos/ee/test/LogSource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package org.jpos.ee.test; | ||
|
||
import org.junit.jupiter.api.extension.ExtendWith; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* Injects a {@link org.jpos.util.Log} object in the declared member or parameter. It needs to be used alongside. <br> | ||
* {@code @ExtendWith(LogSupplierExtension.class)} | ||
* <p> | ||
* Usage example: | ||
* <pre> {@code | ||
* @ExtendWith(LogSupplierExtension.class) | ||
* class XxxxTest ...{ | ||
* ... | ||
* @LogSource | ||
* Log log; | ||
* .... | ||
* | ||
* @AfterEach | ||
* tearDown() { | ||
* log.debug(...); | ||
* } | ||
* } | ||
* }</pre> | ||
* </p> | ||
*/ | ||
@Target({ ElementType.FIELD, ElementType.PARAMETER }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@ExtendWith(LogSupplierExtension.class) | ||
public @interface LogSource { | ||
String realm() default ""; | ||
String logger() default ""; | ||
} |
114 changes: 114 additions & 0 deletions
114
modules/test/src/main/java/org/jpos/ee/test/LogSupplierExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package org.jpos.ee.test; | ||
|
||
import org.jpos.util.Logger; | ||
import org.junit.jupiter.api.extension.*; | ||
|
||
import java.lang.reflect.*; | ||
import java.util.Arrays; | ||
import java.util.function.BiConsumer; | ||
|
||
|
||
public class LogSupplierExtension implements BeforeEachCallback, ParameterResolver, BeforeAllCallback { | ||
|
||
protected static void runOnFields(ExtensionContext context, boolean staticFields, BiConsumer<Field, LogSource> action) { | ||
Arrays.stream(context.getRequiredTestClass().getDeclaredFields()) | ||
.filter(f -> f.isAnnotationPresent(LogSource.class) && Modifier.isStatic(f.getModifiers()) == staticFields) | ||
.forEach(f -> action.accept(f, f.getAnnotation(LogSource.class))); | ||
} | ||
|
||
|
||
protected org.jpos.util.Log getLog(LogSource annotation, Class<?> c) { | ||
Logger logger = annotation.logger().isEmpty() ? TestUtil.getLogger() : Logger.getLogger(annotation.logger()); | ||
String realm = annotation.realm().isEmpty() ? c.getSimpleName() : annotation.realm(); | ||
return new org.jpos.util.Log(logger, realm); | ||
} | ||
|
||
/** | ||
* Called to set up all MUX fields | ||
* @param context The extension context | ||
* @param beforeAll if this is for a beforeAll method set static fields otherwise set instance ones | ||
*/ | ||
protected void setUp(ExtensionContext context, boolean beforeAll) { | ||
runOnFields(context, beforeAll, (field, annotation) -> { | ||
try { | ||
if (!field.isAccessible()) field.setAccessible(true); | ||
field.set( context.getTestInstance().orElse(null), getLog(annotation, context.getRequiredTestClass())); | ||
} catch (IllegalAccessException e) { | ||
throw new IllegalArgumentException(e); | ||
} | ||
}); | ||
|
||
} | ||
|
||
|
||
/** | ||
* Callback that is invoked <em>before</em> an individual test and any | ||
* user-defined setup methods for that test have been executed. | ||
* | ||
* @param context the current extension context; never {@code null} | ||
*/ | ||
@Override | ||
public void beforeEach(ExtensionContext context) throws IllegalArgumentException{ | ||
setUp(context, false); | ||
} | ||
|
||
/** | ||
* Determine if this resolver supports resolution of an argument for the | ||
* {@link Parameter} in the supplied {@link ParameterContext} for the supplied | ||
* {@link ExtensionContext}. | ||
* | ||
* <p>The {@link Method} or {@link Constructor} | ||
* in which the parameter is declared can be retrieved via | ||
* {@link ParameterContext#getDeclaringExecutable()}. | ||
* | ||
* @param parameterContext the context for the parameter for which an argument should | ||
* be resolved; never {@code null} | ||
* @param extensionContext the extension context for the {@code Executable} | ||
* about to be invoked; never {@code null} | ||
* @return {@code true} if this resolver can resolve an argument for the parameter | ||
* @see #resolveParameter | ||
* @see ParameterContext | ||
*/ | ||
@Override | ||
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { | ||
return parameterContext.isAnnotated(LogSource.class) && parameterContext.getTarget().map(Object::getClass).filter(c -> c.isAssignableFrom(LogSource.class)).isPresent(); | ||
} | ||
|
||
/** | ||
* Resolve an argument for the parameter in the supplied {@link ParameterContext} | ||
* for the supplied {@link ExtensionContext}. | ||
* | ||
* <p>This method is only called by the framework if {@link #supportsParameter} | ||
* previously returned {@code true} for the same {@link ParameterContext} | ||
* and {@link ExtensionContext}. | ||
* | ||
* <p>The {@link Method} or {@link Constructor} | ||
* in which the parameter is declared can be retrieved via | ||
* {@link ParameterContext#getDeclaringExecutable()}. | ||
* | ||
* @param parameterContext the context for the parameter for which an argument should | ||
* be resolved; never {@code null} | ||
* @param extensionContext the extension context for the {@code Executable} | ||
* about to be invoked; never {@code null} | ||
* @return the resolved argument for the parameter; may only be {@code null} if the | ||
* parameter type is not a primitive | ||
* @see #supportsParameter | ||
* @see ParameterContext | ||
*/ | ||
@Override | ||
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { | ||
return getLog(parameterContext.getParameter().getAnnotation(LogSource.class), extensionContext.getRequiredTestClass()); | ||
} | ||
|
||
|
||
/** | ||
* Callback that is invoked once <em>before</em> all tests in the current | ||
* container. | ||
* | ||
* @param context the current extension context; never {@code null} | ||
*/ | ||
@Override | ||
public void beforeAll(ExtensionContext context) throws Exception { | ||
setUp(context, true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package org.jpos.ee.test; | ||
|
||
import org.junit.jupiter.api.extension.ExtendWith; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* <p>Injects a muck mux in the declared member or parameter. It needs to be used alongside <br> | ||
* {@code @ExtendWith(MUXSupplierExtension.class)}. </p> | ||
* <p> | ||
* Usage example: | ||
* </p> | ||
* <pre>{@code | ||
* @ExtendWith(MUXSupplierExtension.class) | ||
* class XxxxTest ...{ | ||
* ... | ||
* @MUXMock //register the mux mock in name registrar, if not already registered, and injects it | ||
* MUX mux | ||
* .... | ||
* | ||
* testXxx() { | ||
* //define return for some condition, this example uses mockito | ||
* when(mux.request(same(request), anyLong())).thenReturn(response); | ||
* } | ||
* } | ||
* }</pre> | ||
*/ | ||
@Target({ ElementType.FIELD, ElementType.PARAMETER }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@ExtendWith(MUXSupplierExtension.class) | ||
public @interface MUXMock { | ||
String MUX_NAME = ""; | ||
|
||
/** | ||
* <p>Name by which the mux is going tobe registered in Name Registrar</p> | ||
* | ||
* <p>Don't use the same name for instance and static, unless you don't care the static is unregistered from | ||
* {@link org.jpos.util.NameRegistrar}</p> | ||
* | ||
* Defaults to {@code ""}, in which case one with a random name will be generated | ||
* | ||
* @return the name under which the mux mock will be registered. | ||
*/ | ||
String name() default MUX_NAME; | ||
|
||
/** | ||
* Tells if the mocked mux should return true when its {@code isConnected()} method is called. | ||
* @return the value the mocked mux {@code isConnected()} method should return. | ||
*/ | ||
boolean connected() default true; | ||
} |
Oops, something went wrong.