• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / jmockit1 / 548

29 Nov 2025 11:21PM UTC coverage: 72.124% (-0.08%) from 72.2%
548

Pull #424

github

hazendaz
[junit5] Add new annotation 'ExpectedException'

This annotation enables JUnit 5 tests to specify expected JMockit exceptions (such as MissingInvocation or UnexpectedInvocation or any other) directly on the test method, mirroring the JUnit 4 @test(expected = ...) pattern or ExpectedException rule usage. It simplifies migration of JMockit-based tests from JUnit 4 to JUnit 5, ensuring consistent exception verification for mocking scenarios.

For standard exception assertions unrelated to JMockit, users should continue to use JUnit 5’s assertThrows.

In beforeTestExecution, setup issues will trigger a TestAbortedException when expected match without failing test or attempting to run internals.

handle test execution exception and after test execution will check if expected exception and suppress the exception on match.

Expected exception message can be passed following junit 4 style of contains, if exact match is needed you can disable the messageContains flag
Pull Request #424: Introduction of 'ExpectedException' annotation

5687 of 8390 branches covered (67.78%)

Branch coverage included in aggregate %.

22 of 30 new or added lines in 1 file covered. (73.33%)

14 existing lines in 3 files now uncovered.

11938 of 16047 relevant lines covered (74.39%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.61
/main/src/main/java/mockit/integration/junit4/JUnit4TestRunnerDecorator.java
1
/*
2
 * MIT License
3
 * Copyright (c) 2006-2025 JMockit developers
4
 * See LICENSE file for full license text.
5
 */
6
package mockit.integration.junit4;
7

8
import static mockit.internal.util.StackTrace.filterStackTrace;
9

10
import edu.umd.cs.findbugs.annotations.NonNull;
11
import edu.umd.cs.findbugs.annotations.Nullable;
12

13
import java.lang.reflect.Method;
14

15
import mockit.integration.TestRunnerDecorator;
16
import mockit.internal.expectations.RecordAndReplayExecution;
17
import mockit.internal.faking.FakeInvocation;
18
import mockit.internal.state.SavePoint;
19
import mockit.internal.state.TestRun;
20

21
import org.junit.After;
22
import org.junit.AfterClass;
23
import org.junit.Before;
24
import org.junit.BeforeClass;
25
import org.junit.Test;
26
import org.junit.runners.model.FrameworkMethod;
27

28
final class JUnit4TestRunnerDecorator extends TestRunnerDecorator {
1✔
29
    @Nullable
30
    Object invokeExplosively(@NonNull FakeInvocation invocation, @Nullable Object target, Object... params)
31
            throws Throwable {
32
        FrameworkMethod it = invocation.getInvokedInstance();
1✔
33
        assert it != null;
1!
34

35
        // A @BeforeClass/@AfterClass method:
36
        if (target == null) {
1✔
37
            try {
38
                return executeClassMethod(invocation, params);
1✔
39
            } catch (Throwable t) {
×
40
                filterStackTrace(t);
×
41
                throw t;
×
42
            }
43
        }
44

45
        handleMockingOutsideTestMethods(target);
1✔
46

47
        // A @Before/@After method:
48
        if (it.getAnnotation(Test.class) == null) {
1✔
49
            if (shouldPrepareForNextTest && it.getAnnotation(Before.class) != null) {
1!
50
                prepareToExecuteSetupMethod(target);
1✔
51
            }
52

53
            TestRun.setRunningIndividualTest(target);
1✔
54

55
            try {
56
                invocation.prepareToProceedFromNonRecursiveMock();
1✔
57
                return it.invokeExplosively(target, params);
1✔
58
            } catch (Throwable t) {
×
59
                // noinspection ThrowableNotThrown
60
                RecordAndReplayExecution.endCurrentReplayIfAny();
×
61
                filterStackTrace(t);
×
62
                throw t;
×
63
            } finally {
64
                if (it.getAnnotation(After.class) != null) {
1✔
65
                    shouldPrepareForNextTest = true;
1✔
66
                }
67
            }
68
        }
69

70
        if (shouldPrepareForNextTest) {
1✔
71
            prepareForNextTest();
1✔
72
        }
73

74
        shouldPrepareForNextTest = true;
1✔
75

76
        try {
77
            executeTestMethod(invocation, target, params);
1✔
78
            return null; // it's a test method, therefore has void return type
1✔
UNCOV
79
        } catch (Throwable t) {
×
UNCOV
80
            filterStackTrace(t);
×
UNCOV
81
            throw t;
×
82
        } finally {
83
            TestRun.finishCurrentTestExecution();
1✔
84
        }
85
    }
86

87
    @Nullable
88
    private static Object executeClassMethod(@NonNull FakeInvocation inv, @NonNull Object[] params) throws Throwable {
89
        FrameworkMethod method = inv.getInvokedInstance();
1✔
90
        assert method != null;
1!
91
        handleMockingOutsideTests(method);
1✔
92

93
        TestRun.clearCurrentTestInstance();
1✔
94
        inv.prepareToProceedFromNonRecursiveMock();
1✔
95

96
        return method.invokeExplosively(null, params);
1✔
97
    }
98

99
    private void prepareToExecuteSetupMethod(@NonNull Object target) {
100
        discardTestLevelMockedTypes();
1✔
101
        prepareForNextTest();
1✔
102
        shouldPrepareForNextTest = false;
1✔
103
        createInstancesForTestedFieldsBeforeSetup(target);
1✔
104
    }
1✔
105

106
    private static void handleMockingOutsideTests(@NonNull FrameworkMethod it) {
107
        Class<?> testClass = it.getMethod().getDeclaringClass();
1✔
108

109
        TestRun.enterNoMockingZone();
1✔
110

111
        try {
112
            Class<?> currentTestClass = TestRun.getCurrentTestClass();
1✔
113

114
            if (currentTestClass != null && testClass.isAssignableFrom(currentTestClass)
1✔
115
                    && it.getAnnotation(AfterClass.class) != null) {
1✔
116
                cleanUpMocksFromPreviousTest();
1✔
117
            }
118

119
            if (it.getAnnotation(BeforeClass.class) != null) {
1✔
120
                updateTestClassState(null, testClass);
1✔
121
            }
122
        } finally {
123
            TestRun.exitNoMockingZone();
1✔
124
        }
125
    }
1✔
126

127
    private static void handleMockingOutsideTestMethods(@NonNull Object target) {
128
        Class<?> testClass = target.getClass();
1✔
129

130
        TestRun.enterNoMockingZone();
1✔
131

132
        try {
133
            updateTestClassState(target, testClass);
1✔
134
        } finally {
135
            TestRun.exitNoMockingZone();
1✔
136
        }
137
    }
1✔
138

139
    private static void executeTestMethod(@NonNull FakeInvocation invocation, @NonNull Object testInstance,
140
            @Nullable Object... parameters) throws Throwable {
141
        SavePoint savePoint = new SavePoint();
1✔
142

143
        TestRun.setRunningIndividualTest(testInstance);
1✔
144

145
        FrameworkMethod it = invocation.getInvokedInstance();
1✔
146
        assert it != null;
1!
147
        Method testMethod = it.getMethod();
1✔
148
        Throwable testFailure = null;
1✔
149
        boolean testFailureExpected = false;
1✔
150

151
        try {
152
            createInstancesForTestedFieldsFromBaseClasses(testInstance);
1✔
153
            Object[] annotatedParameters = createInstancesForAnnotatedParameters(testInstance, testMethod, parameters);
1✔
154
            createInstancesForTestedFields(testInstance);
1✔
155

156
            invocation.prepareToProceedFromNonRecursiveMock();
1✔
157

158
            Object[] params = annotatedParameters == null ? parameters : annotatedParameters;
1✔
159
            it.invokeExplosively(testInstance, params);
1✔
UNCOV
160
        } catch (Throwable thrownByTest) {
×
UNCOV
161
            testFailure = thrownByTest;
×
UNCOV
162
            Class<?> expectedType = testMethod.getAnnotation(Test.class).expected();
×
UNCOV
163
            testFailureExpected = expectedType.isAssignableFrom(thrownByTest.getClass());
×
164
        } finally {
165
            concludeTestMethodExecution(savePoint, testFailure, testFailureExpected);
1✔
166
        }
167
    }
1✔
168
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc