diff --git a/src/main/java/io/github/joselion/maybe/SolveHandler.java b/src/main/java/io/github/joselion/maybe/SolveHandler.java index 58be97e..808259e 100644 --- a/src/main/java/io/github/joselion/maybe/SolveHandler.java +++ b/src/main/java/io/github/joselion/maybe/SolveHandler.java @@ -171,6 +171,57 @@ public SolveHandler catchError(final Function hand .leftOrElse(this); } + /** + * If the value was not previously solved, chains another solver function. + * + *

This is helpful to try to solve values in different ways, like when + * nesting a try-catch in another catch block, but more functional. + * + *

The caught error {@code E} is passed in the argument of the + * {@code solver} function. + * + * @param the type of the error the new solver may throw + * @param solver a throwing function that receives the previous error and + * solves another value + * @return a new handler with either the solved value or the error + */ + public SolveHandler onErrorSolve( + final ThrowingFunction solver + ) { + return this.value + .unwrap( + Maybe.partial(solver), + SolveHandler::from + ); + } + + /** + * If the value was not previously solved and the type matches the error + * thrown, chains another solver function. + * + *

This is helpful to try to solve values in different ways, like when + * nesting a try-catch in another catch block, but more functional. + * + * @param the type of the error the new solver may throw + * @param the type of the error to catch + * @param ofType a class instance of the error type to catch + * @param solver a throwing function that receives the previous error and + * solves another value + * @return a new handler with either the solved value or the error + */ + public SolveHandler onErrorSolve( + final Class ofType, + final ThrowingFunction solver + ) { + return this.value + .unwrap( + x -> ofType.isInstance(x) + ? Maybe.of(x).solve(solver) + : SolveHandler.failure(Commons.cast(x)), + SolveHandler::from + ); + } + /** * Chain another solver covering both cases of success or error of the * previous solver in two different callbacks. diff --git a/src/test/java/io/github/joselion/maybe/CloseableHandlerTest.java b/src/test/java/io/github/joselion/maybe/CloseableHandlerTest.java index f18344f..61869b1 100644 --- a/src/test/java/io/github/joselion/maybe/CloseableHandlerTest.java +++ b/src/test/java/io/github/joselion/maybe/CloseableHandlerTest.java @@ -123,7 +123,7 @@ @Nested class when_the_error_is_present { @Test void returns_a_handler_with_the_propagated_error() throws Throwable { final var error = new IOException("Something went wrong..."); - final var solverSpy = Spy.>lambda(fis -> ""); + final var solverSpy = Spy.throwingFunction(fis -> ""); final var handler = CloseableHandler.failure(error) .solve(solverSpy); @@ -176,7 +176,7 @@ @Nested class when_the_error_is_present { @Test void returns_a_handler_with_the_propagated_error() throws Throwable { final var error = new IOException("Something went wrong..."); - final var effectSpy = Spy.>lambda(res -> { }); + final var effectSpy = Spy.throwingConsumer(res -> { }); final var handler = CloseableHandler.failure(error) .effect(effectSpy); diff --git a/src/test/java/io/github/joselion/maybe/EffectHandlerTest.java b/src/test/java/io/github/joselion/maybe/EffectHandlerTest.java index 560c8ef..58401dc 100644 --- a/src/test/java/io/github/joselion/maybe/EffectHandlerTest.java +++ b/src/test/java/io/github/joselion/maybe/EffectHandlerTest.java @@ -5,7 +5,6 @@ import static org.assertj.core.api.InstanceOfAssertFactories.THROWABLE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.nio.file.AccessDeniedException; @@ -15,7 +14,6 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import io.github.joselion.maybe.util.function.ThrowingConsumer; import io.github.joselion.maybe.util.function.ThrowingRunnable; import io.github.joselion.testing.Spy; import io.github.joselion.testing.UnitTest; @@ -66,7 +64,7 @@ Maybe.from(noOp).doOnSuccess(runnableSpy); - verify(runnableSpy, times(1)).run(); + verify(runnableSpy).run(); } } @@ -91,7 +89,7 @@ Maybe.from(throwingOp) .doOnError(FileSystemException.class, consumerSpy); - verify(consumerSpy, times(1)).accept(FAIL_EXCEPTION); + verify(consumerSpy).accept(FAIL_EXCEPTION); } } @@ -114,7 +112,7 @@ Maybe.from(throwingOp) .doOnError(consumerSpy); - verify(consumerSpy, times(1)).accept(FAIL_EXCEPTION); + verify(consumerSpy).accept(FAIL_EXCEPTION); } } } @@ -143,7 +141,7 @@ assertThat(handler.error()).isEmpty(); - verify(consumerSpy, times(1)).accept(FAIL_EXCEPTION); + verify(consumerSpy).accept(FAIL_EXCEPTION); } } @@ -168,7 +166,7 @@ assertThat(handler.error()).isEmpty(); - verify(consumerSpy, times(1)).accept(FAIL_EXCEPTION); + verify(consumerSpy).accept(FAIL_EXCEPTION); } } } @@ -195,9 +193,7 @@ @Test void calls_the_effect_callback_and_returns_a_new_handler() throws FileSystemException { final var effectSpy = Spy.lambda(throwingOp); final var successSpy = Spy.lambda(throwingOp); - final var errorSpy = Spy.>lambda( - err -> throwingOp.run() - ); + final var errorSpy = Spy.throwingConsumer(err -> throwingOp.run()); final var handler = Maybe.from(noOp); final var newHandlers = List.of( handler.effect(effectSpy), @@ -209,8 +205,8 @@ assertThat(newHandler.error()).contains(FAIL_EXCEPTION); }); - verify(effectSpy, times(1)).run(); - verify(successSpy, times(1)).run(); + verify(effectSpy).run(); + verify(successSpy).run(); verify(errorSpy, never()).accept(any()); } } @@ -218,10 +214,8 @@ @Nested class when_the_error_is_present { @Nested class and_the_error_callback_is_provided { @Test void calls_only_the_error_callback_and_returns_a_new_handler() throws FileSystemException { - final var successSpy = Spy.>lambda(() -> { }); - final var errorSpy = Spy.>lambda( - err -> throwingOp.run() - ); + final var successSpy = Spy.throwingRunnable(() -> { }); + final var errorSpy = Spy.throwingConsumer(err -> throwingOp.run()); final var handler = Maybe.from(throwingOp); final var newHandler = handler.effect(successSpy, errorSpy); @@ -229,7 +223,7 @@ assertThat(newHandler.error()).contains(FAIL_EXCEPTION); verify(successSpy, never()).run(); - verify(errorSpy, times(1)).accept(FAIL_EXCEPTION); + verify(errorSpy).accept(FAIL_EXCEPTION); } } @@ -256,7 +250,7 @@ handler.orElse(consumerSpy); - verify(consumerSpy, times(1)).accept(FAIL_EXCEPTION); + verify(consumerSpy).accept(FAIL_EXCEPTION); } } @@ -282,7 +276,7 @@ assertThatCode(handler::orThrow).isEqualTo(FAIL_EXCEPTION); assertThatCode(() -> handler.orThrow(functionSpy)).isEqualTo(anotherError); - verify(functionSpy, times(1)).apply(FAIL_EXCEPTION); + verify(functionSpy).apply(FAIL_EXCEPTION); } } diff --git a/src/test/java/io/github/joselion/maybe/MaybeTest.java b/src/test/java/io/github/joselion/maybe/MaybeTest.java index b8d7154..028deb0 100644 --- a/src/test/java/io/github/joselion/maybe/MaybeTest.java +++ b/src/test/java/io/github/joselion/maybe/MaybeTest.java @@ -6,7 +6,6 @@ import static org.assertj.core.api.InstanceOfAssertFactories.optional; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.FileInputStream; @@ -98,13 +97,13 @@ @Nested class when_a_value_is_provided { @Nested class and_the_operation_succeeds { @Test void returns_a_handler_with_the_value() throws IOException { - final var supplierSpy = Spy.>lambda(() -> OK); + final var supplierSpy = Spy.throwingSupplier(() -> OK); final var handler = Maybe.from(supplierSpy); assertThat(handler.success()).contains(OK); assertThat(handler.error()).isEmpty(); - verify(supplierSpy, times(1)).get(); + verify(supplierSpy).get(); } } @@ -116,7 +115,7 @@ assertThat(handler.success()).isEmpty(); assertThat(handler.error()).contains(FAIL_EXCEPTION); - verify(supplierSpy, times(1)).get(); + verify(supplierSpy).get(); } } } @@ -124,12 +123,12 @@ @Nested class when_an_effect_is_passed { @Nested class and_the_operation_succeeds { @Test void returns_an_empty_handler() { - final var runnableSpy = Spy.>lambda(() -> { }); + final var runnableSpy = Spy.throwingRunnable(() -> { }); final var handler = Maybe.from(runnableSpy); assertThat(handler.error()).isEmpty(); - verify(runnableSpy, times(1)).run(); + verify(runnableSpy).run(); } } @@ -140,7 +139,7 @@ assertThat(handler.error()).contains(FAIL_EXCEPTION); - verify(runnableSpy, times(1)).run(); + verify(runnableSpy).run(); } } } @@ -149,7 +148,7 @@ @Nested class partial { @Nested class when_a_function_is_provided { @Test void returns_a_function_that_takes_a_value_and_returns_a_solve_handler() throws IOException { - final var successSpy = Spy.>lambda(String::length); + final var successSpy = Spy.throwingFunction(String::length); final var failureSpy = Spy.lambda(failFunction); assertThat(Maybe.partial(successSpy).apply(OK)) @@ -161,14 +160,14 @@ .extracting(SolveHandler::error, optional(IOException.class)) .contains(FAIL_EXCEPTION); - verify(successSpy, times(1)).apply(OK); - verify(failureSpy, times(1)).apply(OK); + verify(successSpy).apply(OK); + verify(failureSpy).apply(OK); } } @Nested class when_a_consumer_is_provided { @Test void returns_a_function_that_takes_a_value_and_returns_an_effect_handler() throws IOException { - final var successSpy = Spy.>lambda(v -> { }); + final var successSpy = Spy.throwingConsumer(v -> { }); final var failureSpy = Spy.lambda(failConsumer); assertThat(Maybe.partial(successSpy).apply(OK)) @@ -181,8 +180,8 @@ .extracting(EffectHandler::error, optional(IOException.class)) .contains(FAIL_EXCEPTION); - verify(successSpy, times(1)).accept(OK); - verify(failureSpy, times(1)).accept(OK); + verify(successSpy).accept(OK); + verify(failureSpy).accept(OK); } } } @@ -270,13 +269,13 @@ @Nested class solve { @Nested class when_the_value_is_present { @Test void the_callback_is_called_with_the_value() { - final var functionSpy = Spy.>lambda(v -> OK); + final var functionSpy = Spy.throwingFunction(v -> OK); final var handler = Maybe.of(1).solve(functionSpy); assertThat(handler.success()).contains(OK); assertThat(handler.error()).isEmpty(); - verify(functionSpy, times(1)).apply(1); + verify(functionSpy).apply(1); } } @@ -304,7 +303,7 @@ assertThat(handler.success()).contains(OK); assertThat(handler.error()).isEmpty(); - verify(functionSpy, times(1)).apply(OK); + verify(functionSpy).apply(OK); } } @@ -317,7 +316,7 @@ assertThat(handler.success()).isEmpty(); assertThat(handler.error()).contains(FAIL_EXCEPTION); - verify(functionSpy, times(1)).apply(OK); + verify(functionSpy).apply(OK); } } } @@ -325,19 +324,19 @@ @Nested class effect { @Nested class when_the_value_is_present { @Test void the_callback_is_called_with_the_value() { - final var consumerSpy = Spy.>lambda(v -> { }); + final var consumerSpy = Spy.throwingConsumer(v -> { }); final var handler = Maybe.of(OK) .effect(consumerSpy); assertThat(handler.error()).isEmpty(); - verify(consumerSpy, times(1)).accept(OK); + verify(consumerSpy).accept(OK); } } @Nested class when_the_value_is_not_present { @Test void the_callback_is_never_called_and_returns_a_handler_with_an_error() { - final var consumerSpy = Spy.>lambda(v -> { }); + final var consumerSpy = Spy.throwingConsumer(v -> { }); final var handler = Maybe.empty() .effect(consumerSpy); @@ -352,13 +351,13 @@ @Nested class when_the_new_operation_succeeds { @Test void returns_an_empty_handler() { - final var consumerSpy = Spy.>lambda(v -> { }); + final var consumerSpy = Spy.throwingConsumer(v -> { }); final var handler = Maybe.of(OK) .effect(consumerSpy); assertThat(handler.error()).isEmpty(); - verify(consumerSpy, times(1)).accept(OK); + verify(consumerSpy).accept(OK); } } @@ -370,7 +369,7 @@ assertThat(handler.error()).contains(FAIL_EXCEPTION); - verify(consumerSpy, times(1)).accept(OK); + verify(consumerSpy).accept(OK); } } } diff --git a/src/test/java/io/github/joselion/maybe/SolveHandlerTest.java b/src/test/java/io/github/joselion/maybe/SolveHandlerTest.java index db570c2..db814cf 100644 --- a/src/test/java/io/github/joselion/maybe/SolveHandlerTest.java +++ b/src/test/java/io/github/joselion/maybe/SolveHandlerTest.java @@ -6,7 +6,6 @@ import static org.assertj.core.api.InstanceOfAssertFactories.THROWABLE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.FileInputStream; @@ -19,7 +18,6 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import io.github.joselion.maybe.util.function.ThrowingConsumer; import io.github.joselion.maybe.util.function.ThrowingFunction; import io.github.joselion.maybe.util.function.ThrowingSupplier; import io.github.joselion.testing.Spy; @@ -93,7 +91,7 @@ Maybe.from(okOp) .doOnSuccess(consumerSpy); - verify(consumerSpy, times(1)).accept(OK); + verify(consumerSpy).accept(OK); } } @@ -119,7 +117,7 @@ Maybe.from(throwingOp) .doOnError(FileSystemException.class, consumerSpy); - verify(consumerSpy, times(1)).accept(FAILURE); + verify(consumerSpy).accept(FAILURE); } } @@ -142,7 +140,7 @@ Maybe.from(throwingOp) .doOnError(consumerSpy); - verify(consumerSpy, times(1)).accept(FAILURE); + verify(consumerSpy).accept(FAILURE); } } } @@ -166,21 +164,19 @@ @Nested class and_the_error_is_an_instance_of_the_provided_type { @Test void calls_the_handler_function() { final var functionSpy = Spy.function((FileSystemException e) -> OK); - final var handler = Maybe.from(throwingOp) - .catchError(FileSystemException.class, functionSpy); + final var handler = Maybe.from(throwingOp).catchError(FileSystemException.class, functionSpy); assertThat(handler.success()).contains(OK); assertThat(handler.error()).isEmpty(); - verify(functionSpy, times(1)).apply(FAILURE); + verify(functionSpy).apply(FAILURE); } } @Nested class and_the_error_is_not_an_instance_of_the_provided_type { @Test void never_calls_the_handler_function() { final var functionSpy = Spy.function((AccessDeniedException e) -> OK); - final var handler = Maybe.from(throwingOp) - .catchError(AccessDeniedException.class, functionSpy); + final var handler = Maybe.from(throwingOp).catchError(AccessDeniedException.class, functionSpy); assertThat(handler.success()).isEmpty(); assertThat(handler.error()).contains(FAILURE); @@ -199,7 +195,7 @@ assertThat(solver.success()).contains(OK); assertThat(solver.error()).isEmpty(); - verify(handlerSpy, times(1)).apply(FAILURE); + verify(handlerSpy).apply(FAILURE); } } } @@ -222,12 +218,71 @@ } } + @Nested class onErrorSolve { + @Nested class when_the_error_is_present { + @Nested class and_the_error_type_is_not_provided { + @Test void calls_the_solver_callback_and_returns_a_handler() { + final var solverSpy = Spy.throwingFunction(FileSystemException::getMessage); + final var handler = Maybe.from(throwingOp).onErrorSolve(solverSpy); + + assertThat(handler.success()).contains("FAIL"); + assertThat(handler.error()).isEmpty(); + + verify(solverSpy).apply(FAILURE); + } + } + + @Nested class and_the_error_type_is_provided { + @Nested class and_the_error_is_an_instance_of_the_provided_type { + @Test void calls_the_solver_and_returns_a_handler() { + final var solverSpy = Spy.throwingFunction(FileSystemException::getMessage); + final var handler = Maybe.from(throwingOp).onErrorSolve(FileSystemException.class, solverSpy); + + assertThat(handler.success()).contains("FAIL"); + assertThat(handler.error()).isEmpty(); + + verify(solverSpy).apply(FAILURE); + } + } + + @Nested class and_the_error_is_not_an_instance_of_the_provided_type { + @Test void never_calls_the_solver_and_returns_a_handler_with_the_error() { + final var solverSpy = Spy.throwingFunction(FileSystemException::getMessage); + final var handler = Maybe.from(throwingOp).onErrorSolve(AccessDeniedException.class, solverSpy); + + assertThat(handler.success()).isEmpty(); + assertThat(handler.error()).containsInstanceOf(FileSystemException.class); + + verify(solverSpy, never()).apply(FAILURE); + } + } + } + } + + @Nested class when_the_value_is_present { + @Test void never_calls_the_solver_and_returns_a_handler_with_the_value() { + final var solverSpy = Spy.throwingFunction(RuntimeException::getMessage); + final var handlers = List.of( + Maybe.from(okOp).onErrorSolve(solverSpy), + Maybe.from(okOp).onErrorSolve(RuntimeException.class, solverSpy) + ); + + assertThat(handlers).isNotEmpty().allSatisfy(handler -> { + assertThat(handler.success()).contains(OK); + assertThat(handler.error()).isEmpty(); + }); + + verify(solverSpy, never()).apply(any()); + } + } + } + @Nested class solve { @Nested class when_the_value_is_present { @Test void calls_the_solver_callback_and_returns_a_handler() { - final var solverSpy = Spy.>lambda(String::length); - final var successSpy = Spy.>lambda(String::length); - final var errorSpy = Spy.>lambda(e -> -1); + final var solverSpy = Spy.throwingFunction(String::length); + final var successSpy = Spy.throwingFunction(String::length); + final var errorSpy = Spy.throwingFunction(e -> -1); final var handlers = List.of( Maybe.from(okOp).solve(solverSpy), Maybe.from(okOp).solve(successSpy, errorSpy) @@ -238,8 +293,8 @@ assertThat(handler.error()).isEmpty(); }); - verify(solverSpy, times(1)).apply(OK); - verify(successSpy, times(1)).apply(OK); + verify(solverSpy).apply(OK); + verify(successSpy).apply(OK); verify(errorSpy, never()).apply(any()); } } @@ -247,7 +302,7 @@ @Nested class when_the_error_is_present { @Nested class and_the_error_solver_is_not_provided { @Test void never_calls_the_solver_callback_and_returns_a_handler_with_the_error() { - final var successSpy = Spy.>lambda(String::length); + final var successSpy = Spy.throwingFunction(String::length); final var handler = Maybe.from(throwingOp) .solve(successSpy); @@ -263,8 +318,8 @@ @Nested class and_the_error_solver_is_provided { @Test void call_only_the_error_callback_and_returns_a_new_effect_handler() { - final var successSpy = Spy.>lambda(String::length); - final var errorSpy = Spy.>lambda(e -> -1); + final var successSpy = Spy.throwingFunction(String::length); + final var errorSpy = Spy.throwingFunction(e -> -1); final var handler = Maybe.from(throwingOp) .solve(successSpy, errorSpy); @@ -272,7 +327,7 @@ assertThat(handler.error()).isEmpty(); verify(successSpy, never()).apply(any()); - verify(errorSpy, times(1)).apply(FAILURE); + verify(errorSpy).apply(FAILURE); } } } @@ -281,9 +336,9 @@ @Nested class effect { @Nested class when_the_value_is_present { @Test void calls_the_solver_callback_and_returns_a_handler() throws FileSystemException { - final var effectSpy = Spy.>lambda(v -> throwingOp.get()); - final var successSpy = Spy.>lambda(v -> throwingOp.get()); - final var errorSpy = Spy.>lambda(err -> { }); + final var effectSpy = Spy.throwingConsumer(v -> throwingOp.get()); + final var successSpy = Spy.throwingConsumer(v -> throwingOp.get()); + final var errorSpy = Spy.throwingConsumer(err -> { }); final var handler = Maybe.from(okOp); final var newHandlers = List.of( handler.effect(effectSpy), @@ -294,8 +349,8 @@ assertThat(newHandler.error()).contains(FAILURE); }); - verify(effectSpy, times(1)).accept(OK); - verify(successSpy, times(1)).accept(OK); + verify(effectSpy).accept(OK); + verify(successSpy).accept(OK); verify(errorSpy, never()).accept(any()); } } @@ -303,23 +358,21 @@ @Nested class when_the_error_is_present { @Nested class and_the_error_callback_is_provided { @Test void calls_only_the_error_callback_and_returns_a_handler() throws FileSystemException { - final var successSpy = Spy.>lambda(v -> { }); - final var errorSpy = Spy.>lambda( - err -> throwingOp.get() - ); + final var successSpy = Spy.throwingConsumer(v -> { }); + final var errorSpy = Spy.throwingConsumer(err -> throwingOp.get()); final var handler = Maybe.from(throwingOp); final var newHandler = handler.effect(successSpy, errorSpy); assertThat(newHandler.error()).contains(FAILURE); verify(successSpy, never()).accept(any()); - verify(errorSpy, times(1)).accept(FAILURE); + verify(errorSpy).accept(FAILURE); } } @Nested class and_the_error_callback_is_not_provided { @Test void never_calls_the_effect_callback_and_returns_a_handler_with_the_error() throws FileSystemException { - final var effectSpy = Spy.>lambda(v -> throwingOp.get()); + final var effectSpy = Spy.throwingConsumer(v -> throwingOp.get()); final var handler = Maybe.from(throwingOp); final var newHandler = handler.effect(effectSpy); @@ -454,7 +507,7 @@ assertThat(handler.orElseGet(supplierSpy)).isEqualTo(OTHER); - verify(supplierSpy, times(1)).get(); + verify(supplierSpy).get(); } } } @@ -499,7 +552,7 @@ assertThatCode(handler::orThrow).isEqualTo(FAILURE); assertThatCode(() -> handler.orThrow(functionSpy)).isEqualTo(anotherError); - verify(functionSpy, times(1)).apply(FAILURE); + verify(functionSpy).apply(FAILURE); } } } diff --git a/src/test/java/io/github/joselion/testing/Spy.java b/src/test/java/io/github/joselion/testing/Spy.java index c763e10..0ea2484 100644 --- a/src/test/java/io/github/joselion/testing/Spy.java +++ b/src/test/java/io/github/joselion/testing/Spy.java @@ -8,6 +8,10 @@ import java.util.function.Supplier; import io.github.joselion.maybe.helpers.Commons; +import io.github.joselion.maybe.util.function.ThrowingConsumer; +import io.github.joselion.maybe.util.function.ThrowingFunction; +import io.github.joselion.maybe.util.function.ThrowingRunnable; +import io.github.joselion.maybe.util.function.ThrowingSupplier; public final class Spy { @@ -72,4 +76,60 @@ public static Supplier supplier(final Supplier supplier) { public static Runnable runnable(final Runnable runnable) { return lambda(runnable); } + + /** + * Creates a spy of a generic {@link ThrowingFunction} interface. + * + * @param the type of the input to the function + * @param the type of the result of the function + * @param the type of exception that the function throws + * @param throwingFunction a throwing function to spy + * @return a spy of the provided throwing function + */ + public static ThrowingFunction throwingFunction( + final ThrowingFunction throwingFunction + ) { + return lambda(throwingFunction); + } + + /** + * Creates a spy of a generic {@link ThrowingConsumer} interface. + * + * @param the type of argument passed to the function + * @param the type of exception that the function throws + * @param throwingConsumer a throwing consumer to spy + * @return a spy of the provided throwing consumer + */ + public static ThrowingConsumer throwingConsumer( + final ThrowingConsumer throwingConsumer + ) { + return lambda(throwingConsumer); + } + + /** + * Creates a spy of a generic {@link ThrowingSupplier} interface. + * + * @param the type of results supplied by the supplier + * @param the type of checked exception that the supplier throws + * @param throwingSupplier a throwing supplier to spy + * @return a spy of the provided throwing supplier + */ + public static ThrowingSupplier throwingSupplier( + final ThrowingSupplier throwingSupplier + ) { + return lambda(throwingSupplier); + } + + /** + * Creates a spy of a generic {@link ThrowingRunnable} interface. + * + * @param the type of checked exception that the runnable throws + * @param throwingRunnable a throwing runnable to spy + * @return a spy of the provided throwing runnable + */ + public static ThrowingRunnable throwingRunnable( + final ThrowingRunnable throwingRunnable + ) { + return lambda(throwingRunnable); + } }