001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.test.mock.mockito;
018
019import java.util.List;
020
021import org.mockito.MockSettings;
022import org.mockito.MockingDetails;
023import org.mockito.Mockito;
024import org.mockito.listeners.InvocationListener;
025import org.mockito.listeners.MethodInvocationReport;
026import org.mockito.mock.MockCreationSettings;
027
028import org.springframework.util.Assert;
029
030/**
031 * Reset strategy used on a mock bean. Usually applied to a mock via the
032 * {@link MockBean @MockBean} annotation but can also be directly applied to any mock in
033 * the {@code ApplicationContext} using the static methods.
034 *
035 * @author Phillip Webb
036 * @since 1.4.0
037 * @see ResetMocksTestExecutionListener
038 */
039public enum MockReset {
040
041        /**
042         * Reset the mock before the test method runs.
043         */
044        BEFORE,
045
046        /**
047         * Reset the mock after the test method runs.
048         */
049        AFTER,
050
051        /**
052         * Don't reset the mock.
053         */
054        NONE;
055
056        /**
057         * Create {@link MockSettings settings} to be used with mocks where reset should occur
058         * before each test method runs.
059         * @return mock settings
060         */
061        public static MockSettings before() {
062                return withSettings(BEFORE);
063        }
064
065        /**
066         * Create {@link MockSettings settings} to be used with mocks where reset should occur
067         * after each test method runs.
068         * @return mock settings
069         */
070        public static MockSettings after() {
071                return withSettings(AFTER);
072        }
073
074        /**
075         * Create {@link MockSettings settings} to be used with mocks where a specific reset
076         * should occur.
077         * @param reset the reset type
078         * @return mock settings
079         */
080        public static MockSettings withSettings(MockReset reset) {
081                return apply(reset, Mockito.withSettings());
082        }
083
084        /**
085         * Apply {@link MockReset} to existing {@link MockSettings settings}.
086         * @param reset the reset type
087         * @param settings the settings
088         * @return the configured settings
089         */
090        public static MockSettings apply(MockReset reset, MockSettings settings) {
091                Assert.notNull(settings, "Settings must not be null");
092                if (reset != null && reset != NONE) {
093                        settings.invocationListeners(new ResetInvocationListener(reset));
094                }
095                return settings;
096        }
097
098        /**
099         * Get the {@link MockReset} associated with the given mock.
100         * @param mock the source mock
101         * @return the reset type (never {@code null})
102         */
103        static MockReset get(Object mock) {
104                MockReset reset = MockReset.NONE;
105                MockingDetails mockingDetails = Mockito.mockingDetails(mock);
106                if (mockingDetails.isMock()) {
107                        MockCreationSettings<?> settings = mockingDetails.getMockCreationSettings();
108                        List<InvocationListener> listeners = settings.getInvocationListeners();
109                        for (Object listener : listeners) {
110                                if (listener instanceof ResetInvocationListener) {
111                                        reset = ((ResetInvocationListener) listener).getReset();
112                                }
113                        }
114                }
115                return reset;
116        }
117
118        /**
119         * Dummy {@link InvocationListener} used to hold the {@link MockReset} value.
120         */
121        private static class ResetInvocationListener implements InvocationListener {
122
123                private final MockReset reset;
124
125                ResetInvocationListener(MockReset reset) {
126                        this.reset = reset;
127                }
128
129                public MockReset getReset() {
130                        return this.reset;
131                }
132
133                @Override
134                public void reportInvocation(MethodInvocationReport methodInvocationReport) {
135                }
136
137        }
138
139}