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.function.Supplier; 023 024import org.mockito.MockitoAnnotations; 025 026/** 027 * Definition of a test rule (modification of a test statement). 028 * <p> 029 * TestRule are the only way (outside of the @Parameters concept) to 030 * provides test fixtures, action before and after tests, etc. Only one 031 * <code>TestRule</code> chain is allowed for a test class. If a test class 032 * extends another one, one chain is allowed for each test class and they will 033 * be chained from the upper class to the lower one. 034 * <p> 035 * Conceptually, a test rule, at execution time, will receive a test statement 036 * as parameter and compute another test statement. 037 * <h3>Test Rule chain</h3> 038 * A test rule chain is public final, non static, field, of type 039 * <code>TestRule</code>. Once you have the outer test rule, it is possible to 040 * chain them by using the {@link #around(TestRule)} or 041 * {@link #around(Supplier)} methods. One single TestRule (which can be used as 042 * a start of the chain, or later), can be builded by : 043 * <ul> 044 * <li>Simply using the constructor (or other way) provided by the rule itself 045 * (for instance see the {@link ch.powerunit.rules.TestContextRule 046 * TestContextRule} rule.</li> 047 * <li>Use the <code>before</code> method that will produce a rule to be 048 * executed before a test ; The <code>after</code> method will do the same, but 049 * with a code to be executed after a test.</li> 050 * </ul> 051 * 052 * <h3>Example</h3> 053 * For instance, for this class: 054 * 055 * <pre> 056 * import org.mockito.Mock; 057 * import org.mockito.Mockito; 058 * import ch.powerunit.Rule; 059 * import ch.powerunit.Test; 060 * import ch.powerunit.TestRule; 061 * import ch.powerunit.TestSuite; 062 * 063 * public class MockitoRuleTest implements TestSuite { 064 * public interface Mockable { 065 * void run(); 066 * } 067 * 068 * @Mock 069 * private Mockable mock; 070 * 071 * @Rule 072 * public final TestRule testRule = mockitoRule() 073 * .around(before(this::prepare)); 074 * 075 * public void prepare() { 076 * Mockito.doThrow(new RuntimeException("test")).when(mock).run(); 077 * } 078 * 079 * @Test 080 * public void testException() { 081 * assertWhen((p) -> mock.run()).throwException( 082 * isA(RuntimeException.class)); 083 * } 084 * 085 * } 086 * </pre> 087 * 088 * In this case, the defined chain, will first apply the rule provided by 089 * <code>mockitoRule</code>, and then apply the rule included inside the 090 * <code>around</code>. The <code>before</code> method usage inside the 091 * <code>around</code> will ensure the method <code>prepare</code> is executed 092 * before each test. 093 * <p> 094 * The sequence of execution, will be : 095 * <ol> 096 * <li>Execute the Mockito initialization.</li> 097 * <li>Execute the method <code>prepare</code>.</li> 098 * <li>Execute the test method <code>testException</code>.</li> 099 * </ol> 100 * 101 * @author borettim 102 * 103 */ 104@FunctionalInterface 105public interface TestRule { 106 /** 107 * The default implementation of test rule. 108 * <p> 109 * The goal of this method is to compute another test statement that will be 110 * the one to be runned. 111 * 112 * @param inner 113 * the inner statement 114 * @return the new statement, as modified by this rule. 115 */ 116 Statement<TestContext<Object>, Throwable> computeStatement( 117 Statement<TestContext<Object>, Throwable> inner); 118 119 /** 120 * Build a before testrule. 121 * <p> 122 * The passed piece of code (which can for instance an inline definition or 123 * a reference to a method) that must be executed before the test itself. 124 * 125 * @param before 126 * the code to be used before 127 * @return the rule chain. 128 */ 129 static TestRule before(Runnable before) { 130 return (i) -> (p) -> { 131 before.run();// NOSONAR - It is OK to run here 132 i.run(p); 133 }; 134 } 135 136 /** 137 * Build a after testrule. 138 * <p> 139 * The passed piece of code (which can for instance an inline definition or 140 * a reference to a method) that must be executed after the test itself. 141 * 142 * @param after 143 * the code to be used after 144 * @return the rule chain. 145 */ 146 static TestRule after(Runnable after) { 147 return (i) -> (p) -> { 148 try { 149 i.run(p);// NOSONAR - It is OK to run here 150 } finally { 151 after.run();// NOSONAR - It is OK to run here 152 } 153 }; 154 } 155 156 /** 157 * Add an inner rule. 158 * 159 * @param inner 160 * the inner rule 161 * @return the rule chain. 162 */ 163 default TestRule around(TestRule inner) { 164 return around(() -> inner); 165 } 166 167 /** 168 * Add an inner rule. 169 * <p> 170 * This inner rule is created just before usage, thanks to the 171 * {@link Supplier} object. This can be used for the case when one rule 172 * depend on the outcome of a previous one. 173 * 174 * @param inner 175 * the supplier of the inner rule 176 * @return the rule chain. 177 */ 178 default TestRule around(Supplier<TestRule> inner) { 179 return (i) -> computeStatement((p) -> inner.get().computeStatement(i) 180 .run(p)); 181 } 182 183 /** 184 * Create a rule to support mockito. 185 * 186 * @return the mockitor rule 187 */ 188 static TestRule mockitoRule() { 189 return (i) -> Statement.around(i, (p) -> { 190 MockitoAnnotations.initMocks(p.getTestSuiteObject()); 191 }, (p) -> { 192 // Do nothing as default 193 }); 194 } 195}