TestSuite.java
/**
* 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);
}
}