Skip to content

Commit 0ef1237

Browse files
committed
[integration test] supports application arguments fix jooby-project#1701
1 parent 2908348 commit 0ef1237

6 files changed

Lines changed: 213 additions & 12 deletions

File tree

docs/asciidoc/testing.adoc

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,86 @@ These values can be injected via parameter or instance fields.
398398

399399
The javadoc:JoobyTest[] annotation starts the application using the `test` environment name. You can
400400
creates a `conf/application.test.conf` file to override any other values for testing purpose.
401+
402+
==== Arguments
403+
404+
Application arguments are supported using a `factory method` strategy:
405+
406+
.App
407+
[source,java,role="primary"]
408+
----
409+
public class App extends Jooby {
410+
public App(String argument) { // <1>
411+
get("/", ctx -> "Easy testing!");
412+
}
413+
}
414+
----
415+
416+
.Kotlin
417+
[source,java,role="kotlin"]
418+
----
419+
class App(argument: String): Kooby({ // <1>
420+
get("/") { ctx ->
421+
"Easy testing!"
422+
}
423+
})
424+
----
425+
426+
<1> Application requires a String argument
427+
428+
Write a test:
429+
430+
.TestApp
431+
[source,java,role="primary"]
432+
----
433+
import io.jooby.JoobyTest;
434+
435+
public class TestApp {
436+
437+
@JoobyTest(value = App.class, factoryMethod = "createApp") // <1>
438+
public void test() {
439+
Request request = new Request.Builder()
440+
.url("http://localhost:8911")
441+
.build();
442+
443+
try (Response response = client.newCall(request).execute()) {
444+
assertEquals("Easy testing!", response.body().string());
445+
}
446+
}
447+
448+
public App createApp() { // <2>
449+
return new App("Argument"); // <3>
450+
}
451+
}
452+
----
453+
454+
.Kotlin
455+
[source,kotlin,role="secondary"]
456+
----
457+
import io.jooby.JoobyTest
458+
459+
class TestApp {
460+
461+
@JoobyTest(value = App.class, factoryMethod = "createApp") // <1>
462+
fun test() {
463+
val request = Request.Builder()
464+
.url("http://localhost:8911")
465+
.build()
466+
467+
client.newCall(request).execute().use { response ->
468+
assertEquals("Easy testing!", response.body().string())
469+
}
470+
}
471+
472+
fun createApp() { // <2>
473+
return App("Argument") // <3>
474+
}
475+
}
476+
----
477+
478+
<1> Specify a factory method: `createApp`
479+
<2> Creates the method: must be public and without arguments
480+
<3> Creates your application
481+
482+
If you prefer the annotation at class level (shared application between tests) the factory method
483+
must be static.

modules/jooby-test/src/main/java/io/jooby/JoobyExtension.java

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
1818

1919
import java.lang.reflect.Field;
20+
import java.lang.reflect.InvocationTargetException;
2021
import java.lang.reflect.Method;
2122
import java.lang.reflect.Modifier;
2223
import java.lang.reflect.Parameter;
2324
import java.util.Optional;
2425
import java.util.function.Supplier;
2526

27+
import static io.jooby.SneakyThrows.throwingConsumer;
28+
2629
/**
2730
* JUnit extension that control lifecycle of Jooby applications on tests. The extension shouldn't
2831
* use it directly. Usage is done via {@link JoobyTest} annotation.
@@ -40,24 +43,29 @@ public class JoobyExtension implements BeforeAllCallback, BeforeEachCallback, Af
4043
}
4144

4245
@Override public void beforeAll(ExtensionContext context) throws Exception {
43-
context.getElement().ifPresent(element -> {
46+
context.getElement().ifPresent(throwingConsumer(element -> {
4447
JoobyTest metadata = element.getAnnotation(JoobyTest.class);
4548
if (metadata != null) {
46-
startApp(context, metadata.value(), metadata.environment(),
47-
port(metadata.port(), DEFAULT_PORT), metadata.executionMode());
49+
startApp(context, metadata);
4850
}
49-
});
51+
}));
5052
}
5153

52-
private Jooby startApp(ExtensionContext context, Class applicationClass,
53-
String environment, int port, ExecutionMode executionMode) {
54-
Jooby app = Jooby.createApp(new String[]{environment}, executionMode, applicationClass);
54+
private Jooby startApp(ExtensionContext context, JoobyTest metadata) throws Exception {
55+
Jooby app;
56+
String factoryMethod = metadata.factoryMethod();
57+
if (factoryMethod.isEmpty()) {
58+
app = Jooby.createApp(new String[]{metadata.environment()}, metadata.executionMode(),
59+
metadata.value());
60+
} else {
61+
app = fromFactoryMethod(context, metadata, factoryMethod);
62+
}
5563
ServerOptions serverOptions = app.getServerOptions();
5664
if (serverOptions == null) {
5765
serverOptions = new ServerOptions();
5866
app.setServerOptions(serverOptions);
5967
}
60-
serverOptions.setPort(port);
68+
serverOptions.setPort(port(metadata.port(), DEFAULT_PORT));
6169
Server server = app.start();
6270
ExtensionContext.Store store = getStore(context);
6371
store.put("server", server);
@@ -66,13 +74,12 @@ private Jooby startApp(ExtensionContext context, Class applicationClass,
6674
}
6775

6876
@Override public void beforeEach(ExtensionContext context) throws Exception {
69-
context.getElement().ifPresent(element -> {
77+
context.getElement().ifPresent(throwingConsumer(element -> {
7078
JoobyTest metadata = element.getAnnotation(JoobyTest.class);
7179
if (metadata != null) {
72-
startApp(context, metadata.value(), metadata.environment(),
73-
port(metadata.port(), 0), metadata.executionMode());
80+
startApp(context, metadata);
7481
}
75-
});
82+
}));
7683
}
7784

7885
@Override public void afterAll(ExtensionContext context) throws Exception {
@@ -177,4 +184,28 @@ private int port(int port, int fallback) {
177184
}
178185
}
179186
}
187+
188+
private Jooby fromFactoryMethod(ExtensionContext context, JoobyTest metadata,
189+
String factoryMethod) throws Exception {
190+
Class<?> factoryClass = metadata.factoryClass();
191+
if (factoryClass == Object.class) {
192+
factoryClass = context.getRequiredTestClass();
193+
}
194+
try {
195+
Method factory = factoryClass.getMethod(factoryMethod);
196+
Class<?> returnType = factory.getReturnType();
197+
if (!Jooby.class.isAssignableFrom(returnType)) {
198+
throw new IllegalStateException(
199+
"Factory method must return a Jooby application: " + factory);
200+
}
201+
if (Modifier.isStatic(factory.getModifiers())) {
202+
return (Jooby) factory.invoke(null);
203+
204+
} else {
205+
return (Jooby) factory.invoke(context.getRequiredTestInstance());
206+
}
207+
} catch (InvocationTargetException e) {
208+
throw SneakyThrows.propagate(e);
209+
}
210+
}
180211
}

modules/jooby-test/src/main/java/io/jooby/JoobyTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@
8989
*/
9090
Class<? extends Jooby> value();
9191

92+
Class<?> factoryClass() default Object.class;
93+
94+
String factoryMethod() default "";
95+
9296
/**
9397
* Application environment, default is <code>test</code>.
9498
*

modules/jooby-test/src/test/java/io/jooby/AppPerTestUsingJoobyTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,34 @@ public void sayH2i(int serverPort) throws IOException {
3333
assertEquals("OK", response.body().string());
3434
}
3535
}
36+
37+
@JoobyTest(value = TestArgApp.class, factoryMethod = "createApp")
38+
public void shouldUseFactoryMethod(int serverPort) throws IOException {
39+
Request request = new Request.Builder()
40+
.url("http://localhost:" + serverPort + "/")
41+
.build();
42+
43+
try (Response response = client.newCall(request).execute()) {
44+
assertEquals("TEST", response.body().string());
45+
}
46+
}
47+
48+
@JoobyTest(value = TestArgApp.class, factoryMethod = "createStaticApp")
49+
public void shouldUseStaticFactoryMethod(int serverPort) throws IOException {
50+
Request request = new Request.Builder()
51+
.url("http://localhost:" + serverPort + "/")
52+
.build();
53+
54+
try (Response response = client.newCall(request).execute()) {
55+
assertEquals("TEST", response.body().string());
56+
}
57+
}
58+
59+
public Jooby createApp() {
60+
return new TestArgApp("TEST");
61+
}
62+
63+
public static Jooby createStaticApp() {
64+
return new TestArgApp("TEST");
65+
}
3666
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.jooby;
2+
3+
import okhttp3.OkHttpClient;
4+
import okhttp3.Request;
5+
import okhttp3.Response;
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.io.IOException;
9+
10+
import static org.junit.jupiter.api.Assertions.assertEquals;
11+
12+
@JoobyTest(value = TestArgApp.class, factoryMethod = "createStaticApp")
13+
public class SingleAppFactoryMethodTest {
14+
15+
static OkHttpClient client = new OkHttpClient();
16+
17+
public String serverPath;
18+
19+
public int serverPort;
20+
21+
@Test
22+
public void sayHi() throws IOException {
23+
Request request = new Request.Builder()
24+
.url(serverPath)
25+
.build();
26+
27+
try (Response response = client.newCall(request).execute()) {
28+
assertEquals("TEST", response.body().string());
29+
}
30+
}
31+
32+
@Test
33+
public void sayH2i() throws IOException {
34+
Request request = new Request.Builder()
35+
.url("http://localhost:" + serverPort)
36+
.build();
37+
38+
try (Response response = client.newCall(request).execute()) {
39+
assertEquals("TEST", response.body().string());
40+
}
41+
}
42+
43+
public static Jooby createStaticApp() {
44+
return new TestArgApp("TEST");
45+
}
46+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.jooby;
2+
3+
public class TestArgApp extends Jooby {
4+
public TestArgApp(String name) {
5+
get("/", ctx -> name);
6+
}
7+
}

0 commit comments

Comments
 (0)