/** * 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.util.function.Consumer; import java.util.function.Supplier; import org.mockito.MockitoAnnotations; /** * Definition of a test rule (modification of a test statement). * <p> * TestRule are the only way (outside of the @Parameters concept) to * provides test fixtures, action before and after tests, etc. Only one * <code>TestRule</code> chain is allowed for a test class. If a test class * extends another one, one chain is allowed for each test class and they will * be chained from the upper class to the lower one. * <p> * Conceptually, a test rule, at execution time, will receive a test statement * as parameter and compute another test statement. * <h3>Test Rule chain</h3> * A test rule chain is public final, non static, field, of type * <code>TestRule</code>. Once you have the outer test rule, it is possible to * chain them by using the {@link #around(TestRule)} or * {@link #around(Supplier)} methods. One single TestRule (which can be used as * a start of the chain, or later), can be builded by : * <ul> * <li>Simply using the constructor (or other way) provided by the rule itself * (for instance see the {@link ch.powerunit.rules.TestContextRule * TestContextRule} rule.</li> * <li>Use the <code>before</code> method that will produce a rule to be * executed before a test ; The <code>after</code> method will do the same, but * with a code to be executed after a test.</li> * </ul> * * <h3>Example</h3> * For instance, for this class: * * <pre> * import org.mockito.Mock; * import org.mockito.Mockito; * import ch.powerunit.Rule; * import ch.powerunit.Test; * import ch.powerunit.TestRule; * import ch.powerunit.TestSuite; * * public class MockitoRuleTest implements TestSuite { * public interface Mockable { * void run(); * } * * @Mock * private Mockable mock; * * @Rule * public final TestRule testRule = mockitoRule() * .around(before(this::prepare)); * * public void prepare() { * Mockito.doThrow(new RuntimeException("test")).when(mock).run(); * } * * @Test * public void testException() { * assertWhen((p) -> mock.run()).throwException( * isA(RuntimeException.class)); * } * * } * </pre> * * In this case, the defined chain, will first apply the rule provided by * <code>mockitoRule</code>, and then apply the rule included inside the * <code>around</code>. The <code>before</code> method usage inside the * <code>around</code> will ensure the method <code>prepare</code> is executed * before each test. * <p> * The sequence of execution, will be : * <ol> * <li>Execute the Mockito initialization.</li> * <li>Execute the method <code>prepare</code>.</li> * <li>Execute the test method <code>testException</code>.</li> * </ol> * * @author borettim * */ @FunctionalInterface public interface TestRule { /** * The default implementation of test rule. * <p> * The goal of this method is to compute another test statement that will be * the one to be runned. * * @param inner * the inner statement * @return the new statement, as modified by this rule. */ Statement<TestContext<Object>, Throwable> computeStatement( Statement<TestContext<Object>, Throwable> inner); /** * Build a before testrule. * <p> * The passed piece of code (which can for instance an inline definition or * a reference to a method) that must be executed before the test itself. * * @param before * the code to be used before * @return the rule chain. */ static TestRule before(Runnable before) { return (i) -> (p) -> { before.run();// NOSONAR - It is OK to run here i.run(p); }; } /** * Build a before testrule. * <p> * The passed piece of code (which can for instance an inline definition or * a reference to a method) that must be executed before the test itself. * * @param before * the code to be used before * @return the rule chain. * @since 0.4.0 */ static TestRule before(Consumer<TestContext<Object>> before) { return (i) -> (p) -> { before.accept(p); i.run(p);// NOSONAR - It is OK to run here }; } /** * Build a after testrule. * <p> * The passed piece of code (which can for instance an inline definition or * a reference to a method) that must be executed after the test itself. * * @param after * the code to be used after * @return the rule chain. */ static TestRule after(Runnable after) { return (i) -> (p) -> { try { i.run(p);// NOSONAR - It is OK to run here } finally { after.run();// NOSONAR - It is OK to run here } }; } /** * Build a after testrule. * <p> * The passed piece of code (which can for instance an inline definition or * a reference to a method) that must be executed after the test itself. * * @param after * the code to be used after * @return the rule chain. * @since 0.4.0 */ static TestRule after(Consumer<TestContext<Object>> after) { return (i) -> (p) -> { try { i.run(p);// NOSONAR - It is OK to run here } finally { after.accept(p); } }; } /** * Add an inner rule. * * @param inner * the inner rule * @return the rule chain. */ default TestRule around(TestRule inner) { return around(() -> inner); } /** * Add an inner rule. * <p> * This inner rule is created just before usage, thanks to the * {@link Supplier} object. This can be used for the case when one rule * depend on the outcome of a previous one. * * @param inner * the supplier of the inner rule * @return the rule chain. */ default TestRule around(Supplier<TestRule> inner) { return (i) -> computeStatement((p) -> inner.get().computeStatement(i) .run(p)); } /** * Create a rule to support mockito. * * @return the mockitor rule */ static TestRule mockitoRule() { return (i) -> Statement.around(i, (p) -> { MockitoAnnotations.initMocks(p.getTestSuiteObject()); }, (p) -> { // Do nothing as default }); } }