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 &#64;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 *     &#064;Mock
069 *     private Mockable mock;
070 * 
071 *     &#064;Rule
072 *     public final TestRule testRule = mockitoRule()
073 *             .around(before(this::prepare));
074 * 
075 *     public void prepare() {
076 *         Mockito.doThrow(new RuntimeException(&quot;test&quot;)).when(mock).run();
077 *     }
078 * 
079 *     &#064;Test
080 *     public void testException() {
081 *         assertWhen((p) -&gt; 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}