Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #133 from GuyPaddock/feature/issue-132--let-should…
…-be-lazy Fix Issue #132 -- Make `let` Lazily Evaluated
- Loading branch information
Showing
6 changed files
with
619 additions
and
12 deletions.
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
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
26 changes: 26 additions & 0 deletions
26
src/main/java/com/greghaskins/spectrum/internal/hooks/EagerLetHook.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,26 @@ | ||
package com.greghaskins.spectrum.internal.hooks; | ||
|
||
import com.greghaskins.spectrum.ThrowingSupplier; | ||
|
||
/** | ||
* Implementation of an eager version of {@code let}. | ||
* | ||
* <p>Sematics are the same as with {@link LetHook}, except that all values are calculated at the | ||
* start of the test, rather than on an as-needed basis. | ||
*/ | ||
public class EagerLetHook<T> extends AbstractSupplyingHook<T> { | ||
private final ThrowingSupplier<T> supplier; | ||
|
||
public EagerLetHook(final ThrowingSupplier<T> supplier) { | ||
this.supplier = supplier; | ||
} | ||
|
||
protected T before() { | ||
return supplier.get(); | ||
} | ||
|
||
protected String getExceptionMessageIfUsedAtDeclarationTime() { | ||
return "Cannot use the value from eagerLet() in a suite declaration. " | ||
+ "It may only be used in the context of a running spec."; | ||
} | ||
} |
63 changes: 58 additions & 5 deletions
63
src/main/java/com/greghaskins/spectrum/internal/hooks/LetHook.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 |
---|---|---|
@@ -1,24 +1,77 @@ | ||
package com.greghaskins.spectrum.internal.hooks; | ||
|
||
import com.greghaskins.spectrum.Block; | ||
import com.greghaskins.spectrum.ThrowingSupplier; | ||
import com.greghaskins.spectrum.Variable; | ||
import com.greghaskins.spectrum.internal.DeclarationState; | ||
import com.greghaskins.spectrum.internal.RunReporting; | ||
|
||
import org.junit.runner.Description; | ||
import org.junit.runner.notification.Failure; | ||
|
||
/** | ||
* Implementation of let as a supplying hook. | ||
* Implementation of {@code let} as a supplying hook. | ||
* | ||
* <p>Using {@code let} allows you to define shared values that can be used by multiple tests, | ||
* without having to worry about cleaning up the values between tests to prevent shared state in | ||
* one test from affecting the results of another. | ||
* | ||
* <p>Values are lazily initialized and then cached, so a value is not calculated until the first | ||
* time it is needed in a given test. Subsequent fetches of the value within the same test will | ||
* return the cached value. | ||
*/ | ||
public class LetHook<T> extends AbstractSupplyingHook<T> { | ||
|
||
public class LetHook<T> implements SupplyingHook<T> { | ||
private final ThrowingSupplier<T> supplier; | ||
private final Variable<T> cachedValue = new Variable<>(); | ||
private boolean isCached; | ||
|
||
public LetHook(final ThrowingSupplier<T> supplier) { | ||
this.supplier = supplier; | ||
this.isCached = false; | ||
} | ||
|
||
protected T before() { | ||
return supplier.get(); | ||
@Override | ||
public void accept(final Description description, | ||
final RunReporting<Description, Failure> reporting, final Block block) | ||
throws Throwable { | ||
try { | ||
block.run(); | ||
} finally { | ||
clear(); | ||
} | ||
} | ||
|
||
@Override | ||
public T get() { | ||
assertSpectrumIsRunningTestsNotDeclaringThem(); | ||
|
||
if (!this.isCached) { | ||
this.cachedValue.set(supplier.get()); | ||
|
||
this.isCached = true; | ||
} | ||
|
||
return this.cachedValue.get(); | ||
} | ||
|
||
protected String getExceptionMessageIfUsedAtDeclarationTime() { | ||
return "Cannot use the value from let() in a suite declaration. " | ||
+ "It may only be used in the context of a running spec."; | ||
} | ||
|
||
private void clear() { | ||
this.isCached = false; | ||
this.cachedValue.set(null); | ||
} | ||
|
||
/** | ||
* Will throw an exception if this method happens to be called while Spectrum is still defining | ||
* tests, rather than executing them. Useful to see if a hook is being accidentally used during | ||
* definition. | ||
*/ | ||
private void assertSpectrumIsRunningTestsNotDeclaringThem() { | ||
if (DeclarationState.instance().getCurrentSuiteBeingDeclared() != null) { | ||
throw new IllegalStateException(getExceptionMessageIfUsedAtDeclarationTime()); | ||
} | ||
} | ||
} |
Oops, something went wrong.