Skip to content

Commit

Permalink
Align specification to what it does and improve...
Browse files Browse the repository at this point in the history
mock creation message
  • Loading branch information
leonard84 committed Dec 28, 2024
1 parent c42e602 commit 7d2f94f
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ private <T> T createMockImpl(String inferredName, Class<?> inferredType, T insta
MockImplementation implementation, Map<String, Object> options, Class<?> specifiedType, Closure<?> closure) {
Type effectiveType = specifiedType != null ? specifiedType : options.containsKey("type") ? (Type) options.get("type") : inferredType;
if (effectiveType == null) {
throw new InvalidSpecException("Mock object type cannot be inferred automatically. " +
"Please specify a type explicitly (e.g. 'Mock(Person)').");
throw new InvalidSpecException(nature + " object type cannot be inferred automatically. " +
"Please specify a type explicitly (e.g. '" + nature + "(Person)').");
}
return createMock(inferredName, instance, effectiveType, nature, implementation, options, closure);
}
Expand Down
15 changes: 11 additions & 4 deletions spock-core/src/main/java/org/spockframework/mock/MockNature.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,30 @@ public enum MockNature {
* A mock object whose method calls are verified, which instantiates class-based mock objects with Objenesis,
* and whose strategy for responding to unexpected method calls is {@link ZeroOrNullResponse}.
*/
MOCK(true, true, ZeroOrNullResponse.INSTANCE),
MOCK(true, true, ZeroOrNullResponse.INSTANCE, "Mock"),

/**
* A mock object whose method calls are not verified, which instantiates class-based mock objects with Objenesis,
* and whose strategy for responding to unexpected method calls is {@link EmptyOrDummyResponse}.
*/
STUB(false, true, EmptyOrDummyResponse.INSTANCE),
STUB(false, true, EmptyOrDummyResponse.INSTANCE, "Stub"),

/**
* A mock object whose method calls are verified, which instantiates class-based mock objects by calling a
* real constructor, and whose strategy for responding to unexpected method calls is {@link CallRealMethodResponse}.
*/
SPY(true, false, CallRealMethodResponse.INSTANCE);
SPY(true, false, CallRealMethodResponse.INSTANCE, "Spy");

private final boolean verified;
private final boolean useObjenesis;
private final IDefaultResponse defaultResponse;
private final String methodName;

MockNature(boolean verified, boolean useObjenesis, IDefaultResponse defaultResponse) {
MockNature(boolean verified, boolean useObjenesis, IDefaultResponse defaultResponse, String methodName) {
this.verified = verified;
this.useObjenesis = useObjenesis;
this.defaultResponse = defaultResponse;
this.methodName = methodName;
}

/**
Expand Down Expand Up @@ -79,4 +81,9 @@ public boolean isUseObjenesis() {
public IDefaultResponse getDefaultResponse() {
return defaultResponse;
}

@Override
public String toString() {
return methodName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,124 +14,145 @@

package org.spockframework.smoke.mock

import spock.lang.FailsWith

import org.spockframework.runtime.InvalidSpecException
import spock.lang.Issue
import spock.lang.Specification
import spock.mock.MockMakers

class JavaMocks extends Specification {
class JavaFinalMocks extends Specification {

def "can mock final classes"() {
when:
given:
def person = Mock(FinalPerson)
person.phoneNumber >> 6789

when:
def result = person.phoneNumber

then:
person.phoneNumber == "6789"
1 * person.phoneNumber >> 6789
result == "6789"
}

def "can mock final methods as property with mockito"() {
given:
FinalMethodPerson person = Mock(mockMaker: MockMakers.mockito)
person.phoneNumber >> 6789

expect:
person.phoneNumber == "6789"
when:
def result = person.phoneNumber

then:
1 * person.phoneNumber >> 6789
result == "6789"
}

def "can mock final methods with mockito"() {
given:
FinalMethodPerson person = Mock(mockMaker: MockMakers.mockito)
person.getPhoneNumber() >> 6789

expect:
person.getPhoneNumber() == "6789"
when:
def result = person.getPhoneNumber()

then:
1 * person.getPhoneNumber() >> 6789
result == "6789"
}

def "can mock final methods with mockito with closure"() {
given:
FinalMethodPerson person = Mock(mockMaker: MockMakers.mockito) {
phoneNumber >> 6789
1 * phoneNumber >> 6789
}

expect:
person.phoneNumber == "6789"
}

def "can mock final methods with mockito with closure and specified type"() {
given:
FinalMethodPerson person = Mock(FinalMethodPerson, mockMaker: MockMakers.mockito) {
phoneNumber >> 6789
1 * phoneNumber >> 6789
}

expect:
person.phoneNumber == "6789"
}

@Issue("https://github.com/spockframework/spock/issues/2039")
@FailsWith(
value = InvalidSpecException,
expectedMessage = "The final method 'getPhoneNumber' of 'person' can't be mocked by the 'byte-buddy' mock maker. Please use another mock maker supporting final methods."
)
def "cannot mock final methods with byteBuddy"() {
given:
FinalMethodPerson person = Mock(mockMaker: MockMakers.byteBuddy)

when:
person.getPhoneNumber() >> 6789
person.getPhoneNumber()

then:
InvalidSpecException ex = thrown()
ex.message == "The final method 'getPhoneNumber' of 'person' can't be mocked by the 'byte-buddy' mock maker. Please use another mock maker supporting final methods."

and:
person.getPhoneNumber() == "12345"
1 * person.getPhoneNumber() >> 6789
}

@Issue("https://github.com/spockframework/spock/issues/2039")
def "cannot mock final methods with byteBuddy without error message when one overload is non final"() {

@FailsWith(
value = InvalidSpecException,
expectedMessage = "The final method 'getPhoneNumber' of 'person' can't be mocked by the 'byte-buddy' mock maker. Please use another mock maker supporting final methods."
)
def "cannot mock final method as property with byteBuddy"() {
given:
FinalMethodPerson person = Mock(mockMaker: MockMakers.byteBuddy)

person.finalAndNonFinalOverload() >> "B"
when:
person.phoneNumber

expect:
person.finalAndNonFinalOverload() == "final"
then:
1 * person.phoneNumber >> 6789
}

@Issue("https://github.com/spockframework/spock/issues/2039")
def "non final method overload shall be mockable"() {
@FailsWith(
value = InvalidSpecException,
expectedMessage = "The final method 'isFinalPerson' of 'person' can't be mocked by the 'byte-buddy' mock maker. Please use another mock maker supporting final methods."
)
def "cannot mock final is getter as property with byteBuddy"() {
given:
FinalMethodPerson person = Mock(mockMaker: MockMakers.byteBuddy)

person.finalAndNonFinalOverload("A") >> "B"
when:
person.finalPerson

expect:
person.finalAndNonFinalOverload("A") == "B"
then:
1 * person.finalPerson >> false
}

def "cannot mock final method as property with byteBuddy"() {
@Issue("https://github.com/spockframework/spock/issues/2039")
def "cannot mock final methods with byteBuddy when one overload is non final no error message is produced"() {
given:
FinalMethodPerson person = Mock(mockMaker: MockMakers.byteBuddy)

when:
person.phoneNumber >> 6789
when: "calling the method that has both a final and non final overload"
def result = person.finalAndNonFinalOverload()

then:
InvalidSpecException ex = thrown()
ex.message == "The final method 'getPhoneNumber' of 'person' can't be mocked by the 'byte-buddy' mock maker. Please use another mock maker supporting final methods."
then: "the mocking does not work"
0 * person.finalAndNonFinalOverload() >> "B"

and:
person.phoneNumber == "12345"
and: "the result is not stubbed"
result == "final"
}

def "cannot mock final is getter as property with byteBuddy"() {
@Issue("https://github.com/spockframework/spock/issues/2039")
def "non final method overload shall be mockable"() {
given:
FinalMethodPerson person = Mock(mockMaker: MockMakers.byteBuddy)

when:
person.finalPerson >> false
def result = person.finalAndNonFinalOverload("A")

then:
InvalidSpecException ex = thrown()
ex.message == "The final method 'isFinalPerson' of 'person' can't be mocked by the 'byte-buddy' mock maker. Please use another mock maker supporting final methods."

and:
person.finalPerson
person.finalAndNonFinalOverload("A") >> "B"
result == "B"
}

def "cannot mock final methods without specifying mockMaker"() {
Expand Down

0 comments on commit 7d2f94f

Please sign in to comment.