001/** 002 * Powerunit - A JDK1.8 test framework 003 * Copyright (C) 2014 Mathieu Boretti. 004 * 005 * This file is part of Powerunit 006 * 007 * Powerunit is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * Powerunit is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>. 019 */ 020package ch.powerunit; 021 022import java.util.Arrays; 023import java.util.function.Function; 024import java.util.function.Predicate; 025import java.util.function.Supplier; 026 027import org.hamcrest.Matcher; 028 029import ch.powerunit.helpers.StreamParametersMapFunction; 030import ch.powerunit.rules.SystemPropertiesRule; 031import ch.powerunit.rules.TemporaryFolder; 032import ch.powerunit.rules.TemporaryFolder.TemporaryFolderBuilder; 033import ch.powerunit.rules.impl.TemporaryFolderImpl; 034 035/** 036 * This is the interface to be implemented by test class, in order to have 037 * access to the test DSL. 038 * <p> 039 * This interface is only used to provide the DSL capabilities for the test ; It 040 * is not used as a marker by the framework. 041 * <p> 042 * Several functionalities are provided here : 043 * <ul> 044 * <li>Assertion capabilities (assertion on Object, Iterable, Function, Piece of 045 * code).</li> 046 * <li>Matchers to be used as end parameter of the assertion.</li> 047 * <li><code>{@link #before(Runnable...)}</code> to setup a before action.</li> 048 * <li><code>{@link #after(Runnable...)}</code> to setup a after action.</li> 049 * <li><code>{@link #mockitoRule()}</code> to setup mockito.</li> 050 * <li><code>{@link #systemPropertiesRule(String...)}</code> to restore 051 * properties system after test ; Several others method related to 052 * systemProperties exist.</li> 053 * <li><code>{@link #temporaryFolder()}</code> to support temporary folder.</li> 054 * <li><code>{@link #parametersMap(int, Function)}</code> to create Map function 055 * to be used on the stream to be provided by the {@link Parameters 056 * @Parameters}. Other variants of this method may exist.</li> 057 * </ul> 058 * 059 * @author borettim 060 * 061 */ 062public interface TestSuite extends Assert, Assume, Matchers { 063 064 /** 065 * A static field that is a testsuite (to avoid implementing TestSuite in 066 * test, in the rare case when it may be required). 067 * <p> 068 * The main use case is to access the stream functionnalities from a 069 * {@link Parameters @Parameters} annotated method, has this method must 070 * be static. 071 */ 072 static TestSuite DSL = new TestSuite() { 073 }; 074 075 /** 076 * Build a before testrule. 077 * <p> 078 * The passed runnable will be used before each test. The exact location of 079 * the execution is depending on where this used on the testRule chain. 080 * <p> 081 * In the much simple case (just one method to be executed before each 082 * test), the syntax is : 083 * 084 * <pre> 085 * @Rule 086 * public TestRule rule = before(this::beforeMethodName); 087 * </pre> 088 * 089 * @param befores 090 * the befores 091 * @return {@link TestRule the rule chain}. 092 * @see Rule 093 */ 094 default TestRule before(Runnable... befores) { 095 return Arrays.stream(befores).map(TestRule::before) 096 .reduce((prev, next) -> prev.around(next)).get(); 097 } 098 099 /** 100 * Build a after testrule. 101 * <p> 102 * The passed runnable will be used after each test. The exact location of 103 * the execution is depending on where this used on the testRule chain. 104 * <p> 105 * In the much simple case (just one method to be executed after each test), 106 * the syntax is : 107 * 108 * <pre> 109 * @Rule 110 * public TestRule rule = after(this::afterMethodName); 111 * </pre> 112 * 113 * @param afters 114 * the afters 115 * @return {@link TestRule the rule chain}. 116 * @see Rule 117 */ 118 default TestRule after(Runnable... afters) { 119 return Arrays.stream(afters).map(TestRule::after) 120 .reduce((prev, next) -> prev.around(next)).get(); 121 } 122 123 /** 124 * Create a rule to support mockito. 125 * <p> 126 * This provide a way to setup Mockito before each test. 127 * 128 * @return {@link TestRule the rule chain}. 129 * @see Rule 130 */ 131 default TestRule mockitoRule() { 132 return TestRule.mockitoRule(); 133 } 134 135 /** 136 * Produces a new rule for the temporary folder. 137 * <p> 138 * As the {@link TemporaryFolder} rule provides several methods that are 139 * required for the test, except in the case when only this rule is 140 * required, a direct usage in the rule DSL is not adapted. 141 * 142 * For instance, assuming that it is required to mix a before and the 143 * {@link TemporaryFolder} rule, the code will look like : 144 * 145 * <pre> 146 * private TemporaryFolder temporary = temporaryFolder(); 147 * 148 * @Rule 149 * public TestRule rule = before(this::beforeMethodName).around(temporary); 150 * </pre> 151 * 152 * This is required to ensure that the method of the {@link TemporaryFolder} 153 * object can be used (using the field named <code>temporary</code>). 154 * 155 * @return the temporary folder rule. 156 * @see Rule 157 */ 158 default TemporaryFolder temporaryFolder() { 159 return temporaryFolderBuilder().build(); 160 } 161 162 /** 163 * Produces a new rule builder for the temporary folder. 164 * <p> 165 * As the {@link TemporaryFolder} rule provides several methods that are 166 * required for the test, except in the case when only this rule is 167 * required, a direct usage in the rule DSL is not adapted. 168 * 169 * For instance, assuming that it is required to mix a before and the 170 * {@link TemporaryFolder} rule, the code will look like : 171 * 172 * <pre> 173 * private TemporaryFolder temporary = temporaryFolderBuilder().build(); 174 * 175 * @Rule 176 * public TestRule rule = before(this::beforeMethodName).around(temporary); 177 * </pre> 178 * 179 * This is required to ensure that the method of the {@link TemporaryFolder} 180 * object can be used (using the field named <code>temporary</code>). 181 * <p> 182 * The builder provide several capabilities to create initial folder 183 * structure at the same time than the temporary folder itself. 184 * 185 * @return the temporary folder rule builder. 186 * @see Rule 187 */ 188 default TemporaryFolderBuilder temporaryFolderBuilder() { 189 return new TemporaryFolderImpl.TemporaryFolderBuilderImpl(); 190 } 191 192 /** 193 * Create a rule to restore some system properties after the test 194 * 195 * @param propertiesName 196 * the properties to be restored 197 * @return {@link TestRule the rule chain}. 198 * @see Rule 199 */ 200 default TestRule systemPropertiesRule(String... propertiesName) { 201 return new SystemPropertiesRule(propertiesName); 202 } 203 204 /** 205 * Set a property before the run and ensure correct restore. 206 * 207 * @param propertyName 208 * the name of the property 209 * @param propertyValue 210 * the value of the property 211 * @return {@link TestRule the rule chain}. 212 * @see Rule 213 */ 214 default TestRule systemProperty(String propertyName, 215 Supplier<String> propertyValue) { 216 return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter( 217 propertyName, propertyValue); 218 } 219 220 /** 221 * Set a property before the run and ensure correct restore. 222 * 223 * @param propertyName 224 * the name of the property 225 * @param propertyValue 226 * the value of the property 227 * @return {@link TestRule the rule chain}. 228 * @see Rule 229 */ 230 default TestRule systemProperty(String propertyName, String propertyValue) { 231 return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter( 232 propertyName, propertyValue); 233 } 234 235 /** 236 * Start building a Parameter Mapper function, with an initial converter. 237 * <p> 238 * Not specified index are considered transformed by identity function. 239 * 240 * @param idx 241 * The parameter index 242 * @param mapFunction 243 * the function to be applied 244 * @return the function on the parameter array 245 * @param <T> 246 * The input type for the function 247 * @param <R> 248 * the result type for the function 249 */ 250 default <T, R> StreamParametersMapFunction<T> parametersMap(int idx, 251 Function<T, R> mapFunction) { 252 return StreamParametersMapFunction.map(idx, mapFunction); 253 } 254 255 /** 256 * Start building a Parameter Mapper function, assuming that the input are 257 * String, and using the type of the {@link Parameter @Parameter} field. 258 * <p> 259 * Fields not supported will not be mapped and must be handled manually, 260 * using {@link StreamParametersMapFunction#andMap(int, Function) andMap} 261 * method to avoid any unexpected error. 262 * <p> 263 * The goal of this method is to provide a way to receive so generic 264 * parameter and not having to care about typing. Let's take for example the 265 * following use case : 266 * <ul> 267 * <li>As an input for the test parameters, a CSV file is used. In this 268 * case, a framework like <a 269 * href="http://opencsv.sourceforge.net/">OpenCSV</a> can be used to load 270 * all the parameters, but they are all String.</li> 271 * <li>On the produced stream, the 272 * {@link java.util.stream.Stream#map(Function) map} method can be used to 273 * transform the received data into another format. Here, using the result 274 * of this method as parameter of the map method will ensure the 275 * transformation of the String to the right type, for simple type.</li> 276 * <li>For undetected field type, it is possible to use the method 277 * {@link StreamParametersMapFunction#andMap(int, Function) andMap} (on the 278 * returned object of this method), to add manual transformation.</li> 279 * </ul> 280 * <p> 281 * In this context, as the {@link Parameters @Parameters} annotated 282 * method must be static, access to this method can be done using 283 * {@link #DSL DSL.} prefix. 284 * 285 * @param testClass 286 * the testClass, as this method is to be used in static mode. 287 * @return the function on the parameter array 288 * @see <a href="./helpers/doc-files/convertedType.html">Supported automated 289 * conversion</a> 290 */ 291 default StreamParametersMapFunction<String> stringToParameterMap( 292 Class<?> testClass) { 293 return StreamParametersMapFunction.stringToParameterMap(testClass); 294 } 295 296 /** 297 * Provide a way to add a field to each parameter line. 298 * 299 * @param field 300 * The field to be added. 301 * @return the function that can be used on the stream ( 302 * {@link java.util.stream.Stream#map(Function)}). 303 * @since 0.1.0 304 * @param <T> 305 * The object type to be added. 306 */ 307 default <T> Function<Object[], Object[]> addFieldToEachEntry(T field) { 308 return StreamParametersMapFunction.addFieldToEachEntry(field); 309 } 310 311 /** 312 * Provide a filter for stream parameters, to keep only parameters accepted 313 * by a matcher. 314 * 315 * @param matcher 316 * the matcher 317 * @return the stream filter 318 */ 319 default Predicate<Object[]> parametersFilterUsingMatcher( 320 Matcher<Object[]> matcher) { 321 return matcherPredicate(matcher); 322 } 323 324 /** 325 * Expose a matcher as a predicate. 326 * 327 * @param matcher 328 * the matcher. 329 * @return the predicate 330 * @param <T> 331 * The target object type 332 */ 333 default <T> Predicate<T> matcherPredicate(Matcher<T> matcher) { 334 return matcher::matches; 335 } 336}