/** * Powerunit - A JDK1.8 test framework * Copyright (C) 2014 Mathieu Boretti. * * This file is part of Powerunit * * Powerunit is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Powerunit is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Powerunit. If not, see <http://www.gnu.org/licenses/>. */ package ch.powerunit; import java.io.PrintStream; import java.util.Arrays; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import org.hamcrest.Matcher; import ch.powerunit.exception.AssumptionError; import ch.powerunit.helpers.StreamParametersMapFunction; import ch.powerunit.rules.SystemPropertiesRule; import ch.powerunit.rules.SystemStreamRule; import ch.powerunit.rules.TemporaryFolder; import ch.powerunit.rules.TemporaryFolder.TemporaryFolderBuilder; import ch.powerunit.rules.TestListenerRule; import ch.powerunit.rules.impl.TemporaryFolderImpl; /** * This is the interface to be implemented by test class, in order to have * access to the test DSL. * <p> * This interface is only used to provide the DSL capabilities for the test ; It * is not used as a marker by the framework. * <p> * Several functionalities are provided here : * <ul> * <li>Assertion capabilities (assertion on Object, Iterable, Function, Piece of * code).</li> * <li>Matchers to be used as end parameter of the assertion.</li> * <li><code>{@link #before(Runnable...)}</code> to setup a before action.</li> * <li><code>{@link #after(Runnable...)}</code> to setup a after action.</li> * <li><code>{@link #mockitoRule()}</code> to setup mockito.</li> * <li><code>{@link #systemPropertiesRule(String...)}</code> to restore * properties system after test ; Several others method related to * systemProperties exist.</li> * <li><code>{@link #temporaryFolder()}</code> to support temporary folder.</li> * <li><code>{@link #parametersMap(int, Function)}</code> to create Map function * to be used on the stream to be provided by the {@link Parameters * @Parameters}. Other variants of this method may exist.</li> * <li>Severals method of the form <code>tester...</code> (for instance * <code>{@link #testerOfMatcher(Class)}</code>) that can be used with the * <code>@{@link TestDelegate}</code> annotation to test some standard classes * (like Matchers, Comparator, Function, etc). * </ul> * * @author borettim * */ public interface TestSuite extends Assert, Assume, Matchers, TestFrameworkSupport, ConverterMethod { /** * A static field that is a testsuite (to avoid implementing TestSuite in * test, in the rare case when it may be required). * <p> * The main use case is to access the stream functionnalities from a * {@link Parameters @Parameters} annotated method, as this method must * be static. */ static TestSuite DSL = new TestSuite() { }; /** * Build a before testrule. * <p> * The passed runnable will be used before each test. The exact location of * the execution is depending on where this used on the testRule chain. * <p> * In the much simple case (just one method to be executed before each * test), the syntax is : * * <pre> * @Rule * public TestRule rule = before(this::beforeMethodName); * </pre> * * @param befores * the befores * @return {@link TestRule the rule chain}. * @see Rule */ default TestRule before(Runnable... befores) { return Arrays.stream(befores).map(TestRule::before) .reduce((prev, next) -> prev.around(next)).get(); } /** * Build a before testrule that will receive the {@link TestContext}. * <p> * The passed consumer will be used before each test. The exact location of * the execution is depending on where this used on the testRule chain. * <p> * In the much simple case (just one method to be executed before each * test), the syntax is : * * <pre> * @Rule * public TestRule rule = beforeContextAware(this::beforeMethodName); * </pre> * * @param befores * the befores * @return {@link TestRule the rule chain}. * @see Rule * @since 0.4.0 */ default TestRule beforeContextAware( Consumer<TestContext<Object>>... befores) { return Arrays.stream(befores).map(TestRule::before) .reduce((prev, next) -> prev.around(next)).get(); } /** * Build a after testrule. * <p> * The passed runnable will be used after each test. The exact location of * the execution is depending on where this used on the testRule chain. * <p> * In the much simple case (just one method to be executed after each test), * the syntax is : * * <pre> * @Rule * public TestRule rule = after(this::afterMethodName); * </pre> * * @param afters * the afters * @return {@link TestRule the rule chain}. * @see Rule */ default TestRule after(Runnable... afters) { return Arrays.stream(afters).map(TestRule::after) .reduce((prev, next) -> prev.around(next)).get(); } /** * Build a after testrule that will receive the {@link TestContext}. * <p> * The passed consumer will be used after each test. The exact location of * the execution is depending on where this used on the testRule chain. * <p> * In the much simple case (just one method to be executed after each test), * the syntax is : * * <pre> * @Rule * public TestRule rule = afterContextAware(this::afterMethodName); * </pre> * * @param afters * the afters * @return {@link TestRule the rule chain}. * @see Rule * @since 0.4.0 */ default TestRule afterContextAware(Consumer<TestContext<Object>>... afters) { return Arrays.stream(afters).map(TestRule::after) .reduce((prev, next) -> prev.around(next)).get(); } /** * Create a rule to support mockito. * <p> * This provide a way to setup Mockito before each test. * * @return {@link TestRule the rule chain}. * @see Rule */ default TestRule mockitoRule() { return TestRule.mockitoRule(); } /** * Produces a new rule for the temporary folder. * <p> * As the {@link TemporaryFolder} rule provides several methods that are * required for the test, except in the case when only this rule is * required, a direct usage in the rule DSL is not adapted. * * For instance, assuming that it is required to mix a before and the * {@link TemporaryFolder} rule, the code will look like : * * <pre> * private TemporaryFolder temporary = temporaryFolder(); * * @Rule * public TestRule rule = before(this::beforeMethodName).around(temporary); * </pre> * * This is required to ensure that the method of the {@link TemporaryFolder} * object can be used (using the field named <code>temporary</code>). * * @return the temporary folder rule. * @see Rule */ default TemporaryFolder temporaryFolder() { return temporaryFolderBuilder().build(); } /** * Produces a new rule builder for the temporary folder. * <p> * As the {@link TemporaryFolder} rule provides several methods that are * required for the test, except in the case when only this rule is * required, a direct usage in the rule DSL is not adapted. * * For instance, assuming that it is required to mix a before and the * {@link TemporaryFolder} rule, the code will look like : * * <pre> * private TemporaryFolder temporary = temporaryFolderBuilder().build(); * * @Rule * public TestRule rule = before(this::beforeMethodName).around(temporary); * </pre> * * This is required to ensure that the method of the {@link TemporaryFolder} * object can be used (using the field named <code>temporary</code>). * <p> * The builder provide several capabilities to create initial folder * structure at the same time than the temporary folder itself. * * @return the temporary folder rule builder. * @see Rule */ default TemporaryFolderBuilder temporaryFolderBuilder() { return new TemporaryFolderImpl.TemporaryFolderBuilderImpl(); } /** * Create a rule to restore some system properties after the test * * @param propertiesName * the properties to be restored * @return {@link TestRule the rule chain}. * @see Rule */ default TestRule systemPropertiesRule(String... propertiesName) { return new SystemPropertiesRule(propertiesName); } /** * Set a property before the run and ensure correct restore. * * @param propertyName * the name of the property * @param propertyValue * the value of the property * @return {@link TestRule the rule chain}. * @see Rule */ default TestRule systemProperty(String propertyName, Supplier<String> propertyValue) { return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter( propertyName, propertyValue); } /** * Set a property before the run and ensure correct restore. * * @param propertyName * the name of the property * @param propertyValue * the value of the property * @return {@link TestRule the rule chain}. * @see Rule */ default TestRule systemProperty(String propertyName, String propertyValue) { return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter( propertyName, propertyValue); } /** * Start building a Parameter Mapper function, with an initial converter. * <p> * Not specified index are considered transformed by identity function. * * @param idx * The parameter index * @param mapFunction * the function to be applied * @return the function on the parameter array * @param <T> * The input type for the function * @param <R> * the result type for the function */ default <T, R> StreamParametersMapFunction<T> parametersMap(int idx, Function<T, R> mapFunction) { return StreamParametersMapFunction.map(idx, mapFunction); } /** * Start building a Parameter Mapper function, assuming that the input are * String, and using the type of the {@link Parameter @Parameter} field. * <p> * Fields not supported will not be mapped and must be handled manually, * using {@link StreamParametersMapFunction#andMap(int, Function) andMap} * method to avoid any unexpected error. * <p> * The goal of this method is to provide a way to receive so generic * parameter and not having to care about typing. Let's take for example the * following use case : * <ul> * <li>As an input for the test parameters, a CSV file is used. In this * case, a framework like <a * href="http://opencsv.sourceforge.net/">OpenCSV</a> can be used to load * all the parameters, but they are all String.</li> * <li>On the produced stream, the * {@link java.util.stream.Stream#map(Function) map} method can be used to * transform the received data into another format. Here, using the result * of this method as parameter of the map method will ensure the * transformation of the String to the right type, for simple type.</li> * <li>For undetected field type, it is possible to use the method * {@link StreamParametersMapFunction#andMap(int, Function) andMap} (on the * returned object of this method), to add manual transformation.</li> * </ul> * <p> * In this context, as the {@link Parameters @Parameters} annotated * method must be static, access to this method can be done using * {@link #DSL DSL.} prefix. * * @param testClass * the testClass, as this method is to be used in static mode. * @return the function on the parameter array * @see <a href="./helpers/doc-files/convertedType.html">Supported automated * conversion</a> */ default StreamParametersMapFunction<String> stringToParameterMap( Class<?> testClass) { return StreamParametersMapFunction.stringToParameterMap(testClass); } /** * Provide a way to add a field to each parameter line. * * @param field * The field to be added. * @return the function that can be used on the stream ( * {@link java.util.stream.Stream#map(Function)}). * @since 0.1.0 * @param <T> * The object type to be added. */ default <T> Function<Object[], Object[]> addFieldToEachEntry(T field) { return StreamParametersMapFunction.addFieldToEachEntry(field); } /** * Provide a filter for stream parameters, to keep only parameters accepted * by a matcher. * * @param matcher * the matcher * @return the stream filter */ default Predicate<Object[]> parametersFilterUsingMatcher( Matcher<Object[]> matcher) { return matcherPredicate(matcher); } /** * Expose a matcher as a predicate. * * @param matcher * the matcher. * @return the predicate * @param <T> * The target object type */ default <T> Predicate<T> matcherPredicate(Matcher<T> matcher) { return matcher::matches; } /** * Provide a test rule that suppress both {@link java.lang.System#err system * err} and {@link java.lang.System#out system out}. * * @see ch.powerunit.rules.SystemStreamRule The complete description of the * functionnality of the rule. * * @return the test rule. * @since 0.4.0 */ default SystemStreamRule disableBothStreams() { return SystemStreamRule.disableBothStreams(); } /** * Provide a test rule that replace both {@link java.lang.System#err system * err} and {@link java.lang.System#out system out} with the provided one. * * @param outReplacement * the replacement of the {@link java.lang.System#out system out} * stream. * @param errRemplacement * the replacement of the {@link java.lang.System#err system err} * stream. * * @see ch.powerunit.rules.SystemStreamRule The complete description of the * functionnality of the rule. * * @return the test rule. * @since 0.4.0 */ default SystemStreamRule replaceBothStream(PrintStream outReplacement, PrintStream errRemplacement) { return SystemStreamRule.replaceBothStream(outReplacement, errRemplacement); } /** * Provide a test rule that suppress the {@link java.lang.System#out system * out} stream. * * @see ch.powerunit.rules.SystemStreamRule The complete description of the * functionnality of the rule. * * @return the test rule. * @since 0.4.0 */ default SystemStreamRule disableOutStream() { return SystemStreamRule.disableOutStream(); } /** * Provide a test rule that suppress the {@link java.lang.System#err system * err} stream. * * @see ch.powerunit.rules.SystemStreamRule The complete description of the * functionnality of the rule. * * @return the test rule. * @since 0.4.0 */ default SystemStreamRule disableErrStream() { return SystemStreamRule.disableErrStream(); } /** * Privde a test rule that replace the {@link java.lang.System#out system * out} stream with the provided one * * @param outReplacement * the replacement of the {@link java.lang.System#out system out} * stream. * * @see ch.powerunit.rules.SystemStreamRule The complete description of the * functionnality of the rule. * * @return the test rule. * @since 0.4.0 */ default SystemStreamRule replaceOutStream(PrintStream outReplacement) { return SystemStreamRule.replaceOutStream(outReplacement); } /** * Privde a test rule that replace the {@link java.lang.System#err system * err} stream with the provided one * * @param errReplacement * the replacement of the {@link java.lang.System#err system err} * stream. * * @see ch.powerunit.rules.SystemStreamRule The complete description of the * functionnality of the rule. * * @return the test rule. * @since 0.4.0 */ default SystemStreamRule replaceErrStream(PrintStream errReplacement) { return SystemStreamRule.replaceErrStream(errReplacement); } /** * Build a {@link TestListenerRule} based on the various method. * * @param onStart * {@link TestListenerRule#onStart(TestContext) the action to be * done before the test start}. If null, nothing is done. * @param onEnd * {@link TestListenerRule#onEnd(TestContext) the action to be * done after the test end}. If null, nothing is done. * @param onFailure * {@link TestListenerRule#onFailure(TestContext, AssertionError) * the action to be done in case of failure}. If null, nothing is * done. * @param onError * {@link TestListenerRule#onError(TestContext, Throwable) the * action to be done in case of error}. If null, nothing is done. * @param onAssumptionSkip * {@link TestListenerRule#onAssumptionSkip(TestContext, AssumptionError) * the action to be done in case of assumption skipped}. If null * nothing is done. * @return the Test Rule. * @since 0.4.0 */ default TestListenerRule testListenerRuleOf( Consumer<TestContext<Object>> onStart, Consumer<TestContext<Object>> onEnd, BiConsumer<TestContext<Object>, AssertionError> onFailure, BiConsumer<TestContext<Object>, Throwable> onError, BiConsumer<TestContext<Object>, AssumptionError> onAssumptionSkip) { return TestListenerRule.of(onStart, onEnd, onFailure, onError, onAssumptionSkip); } /** * Build a {@link TestListenerRule} with only an action at start. * * @param onStart * {@link TestListenerRule#onStart(TestContext) the action to be * done before the test start}. * @return the Test Rule. * @since 0.4.0 */ default TestListenerRule testListenerRuleOnStart( Consumer<TestContext<Object>> onStart) { return TestListenerRule.onStart(onStart); } /** * Build a {@link TestListenerRule} with only an action at end. * * @param onEnd * {@link TestListenerRule#onEnd(TestContext) the action to be * done after the test end}. * @return the Test Rule. * @since 0.4.0 */ default TestListenerRule testListenerRuleOnEnd( Consumer<TestContext<Object>> onEnd) { return TestListenerRule.onEnd(onEnd); } }