Skip to content

Commit

Permalink
Implements mocking for constructor invocations.
Browse files Browse the repository at this point in the history
  • Loading branch information
raphw committed Jul 16, 2020
1 parent ddb2acf commit 566f9c0
Show file tree
Hide file tree
Showing 19 changed files with 422 additions and 49 deletions.
18 changes: 18 additions & 0 deletions src/main/java/org/mockito/MockedConstruction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito;

import java.util.List;

/**
* TODO describe interface
*
* @param <T> The type being mocked.
*/
@Incubating
public interface MockedConstruction<T> extends ScopedMock {

List<T> allMocks();
}
10 changes: 1 addition & 9 deletions src/main/java/org/mockito/MockedStatic.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
* @param <T> The type being mocked.
*/
@Incubating
public interface MockedStatic<T> extends AutoCloseable {
public interface MockedStatic<T> extends ScopedMock {

/**
* See {@link Mockito#when(Object)}.
Expand Down Expand Up @@ -63,14 +63,6 @@ default void verify(Verification verification) {
*/
void verifyNoInteractions();

@Override
void close();

/**
* Releases this static mock and is non-operational if already released.
*/
void closeOnDemand();

interface Verification {

void apply() throws Throwable;
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/mockito/Mockito.java
Original file line number Diff line number Diff line change
Expand Up @@ -2120,6 +2120,18 @@ public static <T> MockedStatic<T> mockStatic(Class<T> classToMock, MockSettings
return MOCKITO_CORE.mockStatic(classToMock, mockSettings);
}

@Incubating
@CheckReturnValue
public static <T> MockedConstruction<T> mockConstruction(Class<T> classToMock) {
return mockConstruction(classToMock, withSettings());
}

@Incubating
@CheckReturnValue
public static <T> MockedConstruction<T> mockConstruction(Class<T> classToMock, MockSettings mockSettings) {
return MOCKITO_CORE.mockConstruction(classToMock, mockSettings);
}

/**
* Enables stubbing methods. Use it when you want the mock to return particular value when particular method is called.
* <p>
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/org/mockito/ScopedMock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito;

/**
* Represents a mock with a thread-local explicit scope. Scoped mocks must be closed by the entity
* that activates the scoped mock.
*/
@Incubating
public interface ScopedMock extends AutoCloseable {

/**
* Closes this scoped mock and throws an exception if already closed.
*/
@Override
void close();

/**
* Releases this scoped mock and is non-operational if already released.
*/
void closeOnDemand();
}
59 changes: 59 additions & 0 deletions src/main/java/org/mockito/internal/MockedConstructionImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal;

import java.util.Collections;
import java.util.List;

import org.mockito.MockedConstruction;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.internal.debugging.LocationImpl;
import org.mockito.invocation.Location;
import org.mockito.plugins.MockMaker;

import static org.mockito.internal.util.StringUtil.*;

public final class MockedConstructionImpl<T> implements MockedConstruction<T> {

private final MockMaker.ConstructionMockControl<T> control;

private boolean closed;

private final Location location = new LocationImpl();

protected MockedConstructionImpl(MockMaker.ConstructionMockControl<T> control) {
this.control = control;
}

@Override
public List<T> allMocks() {
return Collections.unmodifiableList(control.getMocks());
}

@Override
public void close() {
assertNotClosed();

closed = true;
control.disable();
}

@Override
public void closeOnDemand() {
if (!closed) {
close();
}
}

private void assertNotClosed() {
if (closed) {
throw new MockitoException(
join(
"The static mock created at",
location.toString(),
"is already resolved and cannot longer be used"));
}
}
}
17 changes: 0 additions & 17 deletions src/main/java/org/mockito/internal/MockedStaticImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
*/
package org.mockito.internal;

import org.mockito.Incubating;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.MockingDetails;
import org.mockito.Mockito;
Expand All @@ -29,21 +27,6 @@
import static org.mockito.internal.util.StringUtil.*;
import static org.mockito.internal.verification.VerificationModeFactory.*;

/**
* Represents an active mock of a type's static methods. The mocking only affects the thread
* on which this static mock was created and it is not safe to use this object from another
* thread. The static mock is released when this object's {@link MockedStaticImpl#close()} method
* is invoked. If this object is never closed, the static mock will remain active on the
* initiating thread. It is therefore recommended to create this object within a try-with-resources
* statement unless when managed explicitly, for example by using a JUnit rule or extension.
* <p>
* If the {@link Mock} annotation is used on fields or method parameters of this type, a static mock
* is created instead of a regular mock. The static mock is activated and released upon completing any
* relevant test.
*
* @param <T> The type being mocked.
*/
@Incubating
public final class MockedStaticImpl<T> implements MockedStatic<T> {

private final MockMaker.StaticMockControl<T> control;
Expand Down
25 changes: 18 additions & 7 deletions src/main/java/org/mockito/internal/MockitoCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@

import static org.mockito.internal.exceptions.Reporter.*;
import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress;
import static org.mockito.internal.util.MockUtil.createMock;
import static org.mockito.internal.util.MockUtil.createStaticMock;
import static org.mockito.internal.util.MockUtil.getInvocationContainer;
import static org.mockito.internal.util.MockUtil.getMockHandler;
import static org.mockito.internal.util.MockUtil.isMock;
import static org.mockito.internal.util.MockUtil.resetMock;
import static org.mockito.internal.util.MockUtil.typeMockabilityOf;
import static org.mockito.internal.util.MockUtil.*;
import static org.mockito.internal.verification.VerificationModeFactory.noInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.noMoreInteractions;

Expand All @@ -21,6 +15,7 @@

import org.mockito.InOrder;
import org.mockito.MockSettings;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.MockingDetails;
import org.mockito.exceptions.misusing.NotAMockException;
Expand Down Expand Up @@ -87,6 +82,22 @@ public <T> MockedStatic<T> mockStatic(Class<T> classToMock, MockSettings setting
return new MockedStaticImpl<>(control);
}

public <T> MockedConstruction<T> mockConstruction(Class<T> classToMock, MockSettings settings) {
if (!MockSettingsImpl.class.isInstance(settings)) {
throw new IllegalArgumentException(
"Unexpected implementation of '"
+ settings.getClass().getCanonicalName()
+ "'\n"
+ "At the moment, you cannot provide your own implementations of that class.");
}
MockSettingsImpl impl = MockSettingsImpl.class.cast(settings);
MockCreationSettings<T> creationSettings = impl.buildStatic(classToMock);
MockMaker.ConstructionMockControl<T> control =
createConstructionMock(classToMock, creationSettings);
control.enable();
return new MockedConstructionImpl<>(control);
}

public <T> OngoingStubbing<T> when(T methodCall) {
MockingProgress mockingProgress = mockingProgress();
mockingProgress.stubbingStarted();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package org.mockito.internal.creation.bytebuddy;

import org.mockito.Incubating;
import org.mockito.internal.util.Supplier;
import org.mockito.invocation.MockHandler;
import org.mockito.mock.MockCreationSettings;

Expand Down Expand Up @@ -51,4 +52,10 @@ public <T> StaticMockControl<T> createStaticMock(
Class<T> type, MockCreationSettings<T> settings, MockHandler handler) {
return defaultByteBuddyMockMaker.createStaticMock(type, settings, handler);
}

@Override
public <T> ConstructionMockControl<T> createConstructionMock(
Class<T> type, MockCreationSettings<T> settings, Supplier<MockHandler<T>> handler) {
return defaultByteBuddyMockMaker.createConstructionMock(type, settings, handler);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ public interface BytecodeGenerator {

<T> Class<? extends T> mockClass(MockFeatures<T> features);

void mockClassConstruction(Class<?> type);

void mockClassStatic(Class<?> type);
}

0 comments on commit 566f9c0

Please sign in to comment.