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 * <li>Severals method of the form <code>tester...</code> (for instance 058 * <code>{@link #testerOfMatcher(Class)}</code>) that can be used with the 059 * <code>@{@link TestDelegate}</code> annotation to test some standard classes 060 * (like Matchers, Comparator, Function, etc). 061 * </ul> 062 * 063 * @author borettim 064 * 065 */ 066public interface TestSuite extends Assert, Assume, Matchers, 067 TestFrameworkSupport { 068 069 /** 070 * A static field that is a testsuite (to avoid implementing TestSuite in 071 * test, in the rare case when it may be required). 072 * <p> 073 * The main use case is to access the stream functionnalities from a 074 * {@link Parameters @Parameters} annotated method, has this method must 075 * be static. 076 */ 077 static TestSuite DSL = new TestSuite() { 078 }; 079 080 /** 081 * Build a before testrule. 082 * <p> 083 * The passed runnable will be used before each test. The exact location of 084 * the execution is depending on where this used on the testRule chain. 085 * <p> 086 * In the much simple case (just one method to be executed before each 087 * test), the syntax is : 088 * 089 * <pre> 090 * @Rule 091 * public TestRule rule = before(this::beforeMethodName); 092 * </pre> 093 * 094 * @param befores 095 * the befores 096 * @return {@link TestRule the rule chain}. 097 * @see Rule 098 */ 099 default TestRule before(Runnable... befores) { 100 return Arrays.stream(befores).map(TestRule::before) 101 .reduce((prev, next) -> prev.around(next)).get(); 102 } 103 104 /** 105 * Build a after testrule. 106 * <p> 107 * The passed runnable will be used after each test. The exact location of 108 * the execution is depending on where this used on the testRule chain. 109 * <p> 110 * In the much simple case (just one method to be executed after each test), 111 * the syntax is : 112 * 113 * <pre> 114 * @Rule 115 * public TestRule rule = after(this::afterMethodName); 116 * </pre> 117 * 118 * @param afters 119 * the afters 120 * @return {@link TestRule the rule chain}. 121 * @see Rule 122 */ 123 default TestRule after(Runnable... afters) { 124 return Arrays.stream(afters).map(TestRule::after) 125 .reduce((prev, next) -> prev.around(next)).get(); 126 } 127 128 /** 129 * Create a rule to support mockito. 130 * <p> 131 * This provide a way to setup Mockito before each test. 132 * 133 * @return {@link TestRule the rule chain}. 134 * @see Rule 135 */ 136 default TestRule mockitoRule() { 137 return TestRule.mockitoRule(); 138 } 139 140 /** 141 * Produces a new rule for the temporary folder. 142 * <p> 143 * As the {@link TemporaryFolder} rule provides several methods that are 144 * required for the test, except in the case when only this rule is 145 * required, a direct usage in the rule DSL is not adapted. 146 * 147 * For instance, assuming that it is required to mix a before and the 148 * {@link TemporaryFolder} rule, the code will look like : 149 * 150 * <pre> 151 * private TemporaryFolder temporary = temporaryFolder(); 152 * 153 * @Rule 154 * public TestRule rule = before(this::beforeMethodName).around(temporary); 155 * </pre> 156 * 157 * This is required to ensure that the method of the {@link TemporaryFolder} 158 * object can be used (using the field named <code>temporary</code>). 159 * 160 * @return the temporary folder rule. 161 * @see Rule 162 */ 163 default TemporaryFolder temporaryFolder() { 164 return temporaryFolderBuilder().build(); 165 } 166 167 /** 168 * Produces a new rule builder for the temporary folder. 169 * <p> 170 * As the {@link TemporaryFolder} rule provides several methods that are 171 * required for the test, except in the case when only this rule is 172 * required, a direct usage in the rule DSL is not adapted. 173 * 174 * For instance, assuming that it is required to mix a before and the 175 * {@link TemporaryFolder} rule, the code will look like : 176 * 177 * <pre> 178 * private TemporaryFolder temporary = temporaryFolderBuilder().build(); 179 * 180 * @Rule 181 * public TestRule rule = before(this::beforeMethodName).around(temporary); 182 * </pre> 183 * 184 * This is required to ensure that the method of the {@link TemporaryFolder} 185 * object can be used (using the field named <code>temporary</code>). 186 * <p> 187 * The builder provide several capabilities to create initial folder 188 * structure at the same time than the temporary folder itself. 189 * 190 * @return the temporary folder rule builder. 191 * @see Rule 192 */ 193 default TemporaryFolderBuilder temporaryFolderBuilder() { 194 return new TemporaryFolderImpl.TemporaryFolderBuilderImpl(); 195 } 196 197 /** 198 * Create a rule to restore some system properties after the test 199 * 200 * @param propertiesName 201 * the properties to be restored 202 * @return {@link TestRule the rule chain}. 203 * @see Rule 204 */ 205 default TestRule systemPropertiesRule(String... propertiesName) { 206 return new SystemPropertiesRule(propertiesName); 207 } 208 209 /** 210 * Set a property before the run and ensure correct restore. 211 * 212 * @param propertyName 213 * the name of the property 214 * @param propertyValue 215 * the value of the property 216 * @return {@link TestRule the rule chain}. 217 * @see Rule 218 */ 219 default TestRule systemProperty(String propertyName, 220 Supplier<String> propertyValue) { 221 return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter( 222 propertyName, propertyValue); 223 } 224 225 /** 226 * Set a property before the run and ensure correct restore. 227 * 228 * @param propertyName 229 * the name of the property 230 * @param propertyValue 231 * the value of the property 232 * @return {@link TestRule the rule chain}. 233 * @see Rule 234 */ 235 default TestRule systemProperty(String propertyName, String propertyValue) { 236 return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter( 237 propertyName, propertyValue); 238 } 239 240 /** 241 * Start building a Parameter Mapper function, with an initial converter. 242 * <p> 243 * Not specified index are considered transformed by identity function. 244 * 245 * @param idx 246 * The parameter index 247 * @param mapFunction 248 * the function to be applied 249 * @return the function on the parameter array 250 * @param <T> 251 * The input type for the function 252 * @param <R> 253 * the result type for the function 254 */ 255 default <T, R> StreamParametersMapFunction<T> parametersMap(int idx, 256 Function<T, R> mapFunction) { 257 return StreamParametersMapFunction.map(idx, mapFunction); 258 } 259 260 /** 261 * Start building a Parameter Mapper function, assuming that the input are 262 * String, and using the type of the {@link Parameter @Parameter} field. 263 * <p> 264 * Fields not supported will not be mapped and must be handled manually, 265 * using {@link StreamParametersMapFunction#andMap(int, Function) andMap} 266 * method to avoid any unexpected error. 267 * <p> 268 * The goal of this method is to provide a way to receive so generic 269 * parameter and not having to care about typing. Let's take for example the 270 * following use case : 271 * <ul> 272 * <li>As an input for the test parameters, a CSV file is used. In this 273 * case, a framework like <a 274 * href="http://opencsv.sourceforge.net/">OpenCSV</a> can be used to load 275 * all the parameters, but they are all String.</li> 276 * <li>On the produced stream, the 277 * {@link java.util.stream.Stream#map(Function) map} method can be used to 278 * transform the received data into another format. Here, using the result 279 * of this method as parameter of the map method will ensure the 280 * transformation of the String to the right type, for simple type.</li> 281 * <li>For undetected field type, it is possible to use the method 282 * {@link StreamParametersMapFunction#andMap(int, Function) andMap} (on the 283 * returned object of this method), to add manual transformation.</li> 284 * </ul> 285 * <p> 286 * In this context, as the {@link Parameters @Parameters} annotated 287 * method must be static, access to this method can be done using 288 * {@link #DSL DSL.} prefix. 289 * 290 * @param testClass 291 * the testClass, as this method is to be used in static mode. 292 * @return the function on the parameter array 293 * @see <a href="./helpers/doc-files/convertedType.html">Supported automated 294 * conversion</a> 295 */ 296 default StreamParametersMapFunction<String> stringToParameterMap( 297 Class<?> testClass) { 298 return StreamParametersMapFunction.stringToParameterMap(testClass); 299 } 300 301 /** 302 * Provide a way to add a field to each parameter line. 303 * 304 * @param field 305 * The field to be added. 306 * @return the function that can be used on the stream ( 307 * {@link java.util.stream.Stream#map(Function)}). 308 * @since 0.1.0 309 * @param <T> 310 * The object type to be added. 311 */ 312 default <T> Function<Object[], Object[]> addFieldToEachEntry(T field) { 313 return StreamParametersMapFunction.addFieldToEachEntry(field); 314 } 315 316 /** 317 * Provide a filter for stream parameters, to keep only parameters accepted 318 * by a matcher. 319 * 320 * @param matcher 321 * the matcher 322 * @return the stream filter 323 */ 324 default Predicate<Object[]> parametersFilterUsingMatcher( 325 Matcher<Object[]> matcher) { 326 return matcherPredicate(matcher); 327 } 328 329 /** 330 * Expose a matcher as a predicate. 331 * 332 * @param matcher 333 * the matcher. 334 * @return the predicate 335 * @param <T> 336 * The target object type 337 */ 338 default <T> Predicate<T> matcherPredicate(Matcher<T> matcher) { 339 return matcher::matches; 340 } 341}