Skip to content

Commit 43d58f7

Browse files
author
Ashley Frieze
committed
Extend with suggestions from Greg
1 parent 8964d27 commit 43d58f7

File tree

7 files changed

+98
-8
lines changed

7 files changed

+98
-8
lines changed

docs/JunitRules.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,6 @@ The rules are applied and the test object created just in time for each atomic t
8080
[SpringSpecJUnitStyle](../src/test/java/specs/SpringSpecJUnitStyle.java) and
8181
[SpringSpecWithRuleClasses](../src/test/java/specs/SpringSpecWithRuleClasses.java)
8282

83-
84-
85-
86-
8783
### What is Supported
8884

8985
* `@ClassRule` is applied
@@ -93,6 +89,7 @@ The rules are applied and the test object created just in time for each atomic t
9389
* `TestRule`s are applied at the level of each atomic test
9490
* `MethodRule`s are applied at the level of each atomic test
9591
* `junitMixin` is implemented to be thread-safe
92+
* `junitMixin` provides an overload for unboxing the supplier - `junitMixin(SomeClass.class, SomeInterface.class)` - if your mixin has an interface to it. See also [`let` - when get is getting you down.](VariablesAndValues.md)
9693

9794
### What is not supported
9895

docs/VariablesAndValues.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,14 @@ it("can use the object as though it was not in a supplier", () -> {
109109
assertThat(list.get(0), is("Hello"));
110110
});
111111
```
112+
113+
The `unbox` method can be used with any `Supplier<>`. There is also an overload of `let` as a short form for this:
114+
115+
```java
116+
List<String> list = let(ArrayList::new, List.class);
117+
118+
it("can use the object as though it was not in a supplier", () -> {
119+
list.add("Hello");
120+
assertThat(list.get(0), is("Hello"));
121+
});
122+
```

src/main/java/com/greghaskins/spectrum/Configure.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.greghaskins.spectrum;
22

3+
import static com.greghaskins.spectrum.Unboxer.unbox;
4+
35
import com.greghaskins.spectrum.internal.DeclarationState;
46
import com.greghaskins.spectrum.internal.configuration.BlockFocused;
57
import com.greghaskins.spectrum.internal.configuration.BlockIgnore;
@@ -140,4 +142,22 @@ static FilterConfigurationChain excludeTags(String... tagsToExclude) {
140142
static <T> Supplier<T> junitMixin(final Class<T> classWithRules) {
141143
return Rules.applyRules(classWithRules, DeclarationState.instance()::addHook);
142144
}
145+
146+
/**
147+
* Uses the given class as a mix-in for JUnit rules to be applied. These rules will cascade down
148+
* and be applied at the level of specs or atomic specs. Provide a proxy to the eventual mix-in
149+
* via the interface given.
150+
*
151+
* @param classWithRules Class to create and apply rules to for each spec.
152+
* @param interfaceToReturn the type of interface the caller requires the proxy object to be
153+
* @param <T> type of the mixin object
154+
* @param <R> the required type at the consumer - allowing for generic interfaces
155+
* (e.g. <code>List&lt;String&gt;</code>)
156+
* @param <S> type of the interface common to the rules object and the proxy
157+
* @return a proxy to the rules object
158+
*/
159+
static <T extends S, R extends S, S> R junitMixin(final Class<T> classWithRules,
160+
final Class<S> interfaceToReturn) {
161+
return unbox(junitMixin(classWithRules), interfaceToReturn);
162+
}
143163
}

src/main/java/com/greghaskins/spectrum/Spectrum.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.greghaskins.spectrum;
22

3+
import static com.greghaskins.spectrum.Unboxer.unbox;
4+
35
import com.greghaskins.spectrum.dsl.specification.Specification;
46
import com.greghaskins.spectrum.internal.DeclarationState;
57
import com.greghaskins.spectrum.internal.Suite;
@@ -234,6 +236,33 @@ public static <T> Supplier<T> let(final com.greghaskins.spectrum.ThrowingSupplie
234236
return Specification.let(supplier);
235237
}
236238

239+
/**
240+
* A value that will be fresh within each spec and cannot bleed across specs. This is provided
241+
* as a proxy to a lazy loading object, with the interface given.
242+
*
243+
* <p>
244+
* Note that {@code let} is lazy-evaluated: the internal {@code supplier} is not called until the first
245+
* time it is used.
246+
* </p>
247+
*
248+
* @param <T> The type of value
249+
* @param <R> the required type at the consumer - allowing for generic interfaces
250+
* (e.g. <code>List&lt;String&gt;</code>)
251+
* @param <S> the type of the value's interface.
252+
* @param supplier {@link com.greghaskins.spectrum.ThrowingSupplier} function that either
253+
* generates the value, or throws a {@link Throwable}
254+
* @param interfaceToUse an interface type, that is supported by the supplied object and can be used
255+
* to access it through the supplier without having to call {@link Supplier#get()}
256+
* @return proxy to the object supplied, using the interface given
257+
* @see Specification#let
258+
* @see Unboxer#unbox(Supplier, Class)
259+
*/
260+
public static <T extends S, R extends S, S> R let(
261+
final com.greghaskins.spectrum.ThrowingSupplier<T> supplier,
262+
Class<S> interfaceToUse) {
263+
return unbox(let(supplier), interfaceToUse);
264+
}
265+
237266
private final Suite rootSuite;
238267

239268
/**

src/main/java/com/greghaskins/spectrum/Unboxer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public interface Unboxer {
2424
*/
2525
@SuppressWarnings("unchecked")
2626
static <T extends S, R extends S, S> R unbox(Supplier<T> supplier, Class<S> asClass) {
27-
return (R) Proxy.newProxyInstance(asClass.getClassLoader(), new Class[] {asClass},
27+
return (R) Proxy.newProxyInstance(asClass.getClassLoader(), new Class<?>[] {asClass},
2828
(Object proxy, Method method, Object[] args) -> method.invoke(supplier.get(), args));
2929
}
3030
}

src/test/java/specs/JUnitRuleExample.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,19 @@
2121

2222
@RunWith(Spectrum.class)
2323
public class JUnitRuleExample {
24+
public interface JUnitTempFolderInterface {
25+
TemporaryFolder getTempFolder();
26+
}
27+
2428
// mixins for the Spectrum native style of mixin
25-
public static class TempFolderRuleMixin {
29+
public static class TempFolderRuleMixin implements JUnitTempFolderInterface {
2630
@Rule
2731
public TemporaryFolder tempFolderRule = new TemporaryFolder();
32+
33+
@Override
34+
public TemporaryFolder getTempFolder() {
35+
return tempFolderRule;
36+
}
2837
}
2938

3039
public static class JUnitBeforeClassExample {
@@ -77,11 +86,26 @@ public static void beforeClass() {
7786
assertThat(classValue, is("initialised"));
7887
});
7988
});
89+
90+
describe("A spec with a rule mix-in via interface", () -> {
91+
JUnitTempFolderInterface tempFolderGetter =
92+
junitMixin(TempFolderRuleMixin.class, JUnitTempFolderInterface.class);
93+
94+
it("has access to the rule-provided object at the top level", () -> {
95+
checkCanUseTempFolderAndRecordWhatItWas(ruleProvidedFoldersSeen,
96+
tempFolderGetter.getTempFolder());
97+
});
98+
});
8099
}
81100

82101
private void checkCanUseTempFolderAndRecordWhatItWas(Set<File> filesSeen,
83102
Supplier<TempFolderRuleMixin> tempFolderRuleMixin) {
84-
assertNotNull(tempFolderRuleMixin.get().tempFolderRule.getRoot());
85-
filesSeen.add(tempFolderRuleMixin.get().tempFolderRule.getRoot());
103+
checkCanUseTempFolderAndRecordWhatItWas(filesSeen, tempFolderRuleMixin.get().tempFolderRule);
104+
}
105+
106+
private void checkCanUseTempFolderAndRecordWhatItWas(Set<File> filesSeen,
107+
TemporaryFolder folder) {
108+
assertNotNull(folder.getRoot());
109+
filesSeen.add(folder.getRoot());
86110
}
87111
}

src/test/java/specs/UnboxerSpecs.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ public class UnboxerSpecs {
3636
});
3737
});
3838

39+
describe("Using let overload", () -> {
40+
List<String> list = let(ArrayList::new, List.class);
41+
42+
it("can use the object as though it was not in a supplier", () -> {
43+
list.add("Hello");
44+
assertThat(list.get(0), is("Hello"));
45+
});
46+
});
47+
3948
describe("Using unboxer with variable", () -> {
4049
Variable<ArrayList<String>> listVariable = new Variable<>(new ArrayList<>());
4150
List<String> list = unbox(listVariable, List.class);

0 commit comments

Comments
 (0)