Skip to content

Commit 2e44bdf

Browse files
committed
feat: Add JUnit5 extension for OpenFeature
Signed-off-by: Simon Schrottner <[email protected]>
1 parent b9028e5 commit 2e44bdf

File tree

12 files changed

+641
-2
lines changed

12 files changed

+641
-2
lines changed

Diff for: pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
<modules>
3030
<module>hooks/open-telemetry</module>
31+
<module>tools/junit-openfeature</module>
3132
<module>providers/flagd</module>
3233
<module>providers/flagsmith</module>
3334
<module>providers/go-feature-flag</module>

Diff for: providers/flagd/schemas

Diff for: providers/flagd/test-harness

Diff for: tools/junit-openfeature/README.md

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# JUnit Open Feature extension
2+
3+
A JUnit5 extension to reduce boilerplate code for testing code which utilizes OpenFeature.
4+
5+
## Getting Started
6+
7+
We are supporting two different flavors for testing, a [simple](#simple-configuration) and an [extended](#extended-configuration) configuration.
8+
9+
### Simple Configuration
10+
11+
Choose the simple configuration if you are only testing in one domain.
12+
Per default, it will be used in the global domain.
13+
14+
```java
15+
@Test
16+
@Flag(name = "BOOLEAN_FLAG", value = "true")
17+
void test() {
18+
// your test code
19+
}
20+
```
21+
22+
#### Multiple flags
23+
24+
The `@Flag` annotation can be also repeated multiple times.
25+
26+
```java
27+
@Test
28+
@Flag(name = "BOOLEAN_FLAG", value = "true")
29+
@Flag(name = "BOOLEAN_FLAG2", value = "true")
30+
void test() {
31+
// your test code
32+
}
33+
```
34+
35+
#### Defining Flags for a whole test-class
36+
37+
`@Flags` can be defined on the class-level too, but method-level
38+
annotations will superseded class-level annotations.
39+
40+
```java
41+
@Flag(name = "BOOLEAN_FLAG", value = "true")
42+
@Flag(name = "BOOLEAN_FLAG2", value = "false")
43+
class Test {
44+
@Test
45+
@Flag(name = "BOOLEAN_FLAG2", value = "true") // will be used
46+
void test() {
47+
// your test code
48+
}
49+
}
50+
```
51+
52+
#### Setting a different domain
53+
54+
You can define an own domain on the test-class-level with `@OpenFeatureDefaultDomain` like:
55+
56+
```java
57+
@OpenFeatureDefaultDomain("domain")
58+
class Test {
59+
@Test
60+
@Flag(name = "BOOLEAN_FLAG", value = "true")
61+
// this flag will be available in the `domain` domain
62+
void test() {
63+
// your test code
64+
}
65+
}
66+
```
67+
68+
### Extended Configuration
69+
70+
Use the extended configuration when your code needs to use multiple domains.
71+
72+
```java
73+
@Test
74+
@OpenFeature({
75+
@Flag(name = "BOOLEAN_FLAG", value = "true")
76+
})
77+
void test() {
78+
// your test code
79+
}
80+
```
81+
82+
83+
#### Multiple flags
84+
85+
The `@Flag` annotation can be also repeated multiple times.
86+
87+
```java
88+
@Test
89+
@OpenFeature({
90+
@Flag(name = "BOOLEAN_FLAG", value = "true"),
91+
@Flag(name = "BOOLEAN_FLAG2", value = "true")
92+
})
93+
void test() {
94+
// your test code
95+
}
96+
```
97+
98+
#### Defining Flags for a whole test-class
99+
100+
`@Flags` can be defined on the class-level too, but method-level
101+
annotations will superseded class-level annotations.
102+
103+
```java
104+
@OpenFeature({
105+
@Flag(name = "BOOLEAN_FLAG", value = "true"),
106+
@Flag(name = "BOOLEAN_FLAG2", value = "false")
107+
})
108+
class Test {
109+
@Test
110+
@OpenFeature({
111+
@Flag(name = "BOOLEAN_FLAG2", value = "true") // will be used
112+
})
113+
void test() {
114+
// your test code
115+
}
116+
}
117+
```
118+
119+
#### Setting a different domain
120+
121+
You can define an own domain for each usage of the `@OpenFeature` annotation with the `domain` property:
122+
123+
```java
124+
@Test
125+
@OpenFeature(
126+
domain = "domain",
127+
value = {
128+
@Flag(name = "BOOLEAN_FLAG2", value = "true") // will be used
129+
})
130+
// this flag will be available in the `domain` domain
131+
void test() {
132+
// your test code
133+
}
134+
```
135+
136+
#### Multiple Configurations for multiple domains
137+
138+
Following testcode will generate two providers, with different flag configurations for a test.
139+
140+
```java
141+
@Test
142+
@OpenFeature({
143+
@Flag(name = "BOOLEAN_FLAG", value = "true"),
144+
@Flag(name = "BOOLEAN_FLAG2", value = "true")
145+
})
146+
@OpenFeature(
147+
domain = "domain",
148+
value = {
149+
@Flag(name = "BOOLEAN_FLAG2", value = "true") // will be used
150+
})
151+
void test() {
152+
// your test code
153+
}
154+
```
155+

Diff for: tools/junit-openfeature/pom.xml

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>dev.openfeature.contrib</groupId>
7+
<artifactId>parent</artifactId>
8+
<version>0.1.0</version>
9+
<relativePath>../../pom.xml</relativePath>
10+
</parent>
11+
<groupId>dev.openfeature.contrib.tools</groupId>
12+
<artifactId>junitopenfeature</artifactId>
13+
<version>3.1.2</version> <!--x-release-please-version -->
14+
15+
<name>junit-openfeature-extension</name>
16+
<description>JUnit5 Extension for OpenFeature</description>
17+
<url>https://openfeature.dev</url>
18+
19+
<developers>
20+
<developer>
21+
<id>aepfli</id>
22+
<name>Simon Schrottner</name>
23+
<organization>OpenFeature</organization>
24+
<url>https://openfeature.dev/</url>
25+
</developer>
26+
</developers>
27+
28+
<dependencies>
29+
<dependency>
30+
<groupId>dev.openfeature</groupId>
31+
<artifactId>sdk</artifactId>
32+
<version>[1.4,2.0)</version>
33+
<scope>provided</scope>
34+
</dependency>
35+
36+
<dependency>
37+
<groupId>org.junit.jupiter</groupId>
38+
<artifactId>junit-jupiter-api</artifactId>
39+
<version>${junit.jupiter.version}</version>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>org.junit-pioneer</groupId>
44+
<artifactId>junit-pioneer</artifactId>
45+
<version>1.9.1</version>
46+
</dependency>
47+
</dependencies>
48+
49+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dev.openfeature.contrib.tools.junitopenfeature;
2+
3+
import org.junit.jupiter.api.extension.ExtendWith;
4+
5+
import java.lang.annotation.*;
6+
7+
@Target({ElementType.METHOD, ElementType.TYPE})
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Repeatable(Flags.class)
10+
@ExtendWith(OpenFeatureExtension.class)
11+
public @interface Flag {
12+
String name();
13+
14+
String value();
15+
16+
Class<?> valueType() default Boolean.class;
17+
}
18+
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dev.openfeature.contrib.tools.junitopenfeature;
2+
3+
import org.junit.jupiter.api.extension.ExtendWith;
4+
5+
import java.lang.annotation.ElementType;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
@Target({ElementType.METHOD, ElementType.TYPE})
11+
@Retention(RetentionPolicy.RUNTIME)
12+
@ExtendWith(OpenFeatureExtension.class)
13+
public @interface Flags {
14+
Flag[] value() default {};
15+
}
16+
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dev.openfeature.contrib.tools.junitopenfeature;
2+
3+
import dev.openfeature.sdk.FlagValueType;
4+
import org.junit.jupiter.api.extension.ExtendWith;
5+
6+
import java.lang.annotation.*;
7+
8+
@Target({ ElementType.METHOD, ElementType.TYPE })
9+
@Retention(RetentionPolicy.RUNTIME)
10+
@Repeatable(value = OpenFeatures.class)
11+
@ExtendWith(OpenFeatureExtension.class)
12+
public @interface OpenFeature {
13+
String domain() default "";
14+
Flag[] value();
15+
}
16+
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package dev.openfeature.contrib.tools.junitopenfeature;
2+
3+
import org.junit.jupiter.api.extension.ExtendWith;
4+
5+
import java.lang.annotation.*;
6+
7+
@Target({ElementType.TYPE})
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@ExtendWith(OpenFeatureExtension.class)
10+
public @interface OpenFeatureDefaultDomain {
11+
String value() default "";
12+
}
13+
14+

0 commit comments

Comments
 (0)