-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Fix test failure with static mocking by replacing PowerMock
with Mockito
#4254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,23 @@ | ||
package com.fasterxml.jackson.databind.deser.lazy; | ||
|
||
import java.math.BigDecimal; | ||
|
||
import org.mockito.MockedStatic; | ||
import org.mockito.Mockito; | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.annotation.JsonUnwrapped; | ||
import com.fasterxml.jackson.core.io.NumberInput; | ||
|
||
import com.fasterxml.jackson.databind.BaseMapTest; | ||
import com.fasterxml.jackson.databind.DatabindException; | ||
import com.fasterxml.jackson.databind.DeserializationFeature; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.ObjectReader; | ||
import com.fasterxml.jackson.databind.*; | ||
import com.fasterxml.jackson.databind.json.JsonMapper; | ||
|
||
import org.junit.Ignore; | ||
import org.junit.runner.RunWith; | ||
import org.mockito.Mockito; | ||
import org.powermock.core.classloader.annotations.PrepareForTest; | ||
import org.powermock.modules.junit4.PowerMockRunner; | ||
|
||
import static org.powermock.api.mockito.PowerMockito.*; | ||
|
||
import java.math.BigDecimal; | ||
import static org.mockito.Mockito.mockStatic; | ||
|
||
/** | ||
* Tests to verify that skipping of unknown/unmapped works such that | ||
* "expensive" numbers (all floating-point, {@code BigInteger}) is avoided. | ||
*/ | ||
@RunWith(PowerMockRunner.class) | ||
@PrepareForTest(fullyQualifiedNames = "com.fasterxml.jackson.core.io.NumberInput") | ||
@Ignore("Powermock stopped working with NumberUtil refactoring see [databind#4250]") | ||
public class LazyIgnoralForNumbers3730Test extends BaseMapTest | ||
{ | ||
static class ExtractFieldsNoDefaultConstructor3730 { | ||
|
@@ -103,103 +92,115 @@ static class Values { | |
|
||
public void testIgnoreBigInteger() throws Exception | ||
{ | ||
final String MOCK_MSG = "mock: deliberate failure for parseBigInteger"; | ||
mockStatic(NumberInput.class); | ||
when(NumberInput.parseBigInteger(Mockito.anyString())) | ||
.thenThrow(new IllegalStateException(MOCK_MSG)); | ||
when(NumberInput.parseBigInteger(Mockito.anyString(), Mockito.anyBoolean())) | ||
.thenThrow(new IllegalStateException(MOCK_MSG)); | ||
StringBuilder stringBuilder = new StringBuilder(); | ||
for (int i = 0; i < 999; i++) { | ||
stringBuilder.append(7); | ||
} | ||
final String testBigInteger = stringBuilder.toString(); | ||
final String json = genJson(testBigInteger); | ||
ExtractFieldsNoDefaultConstructor3730 ef = | ||
MAPPER.readValue(json, ExtractFieldsNoDefaultConstructor3730.class); | ||
assertNotNull(ef); | ||
// Ok but then let's ensure method IS called, if field is actually mapped, | ||
// first to Number | ||
try { | ||
Object ob = STRICT_MAPPER.readValue(json, UnwrappedWithNumber.class); | ||
fail("Should throw exception with mocking: instead got: "+MAPPER.writeValueAsString(ob)); | ||
} catch (DatabindException e) { | ||
verifyMockException(e, MOCK_MSG); | ||
try (MockedStatic<NumberInput> mocked = mockStatic(NumberInput.class)) { | ||
// Set up, mock NumberInput.parseBigInteger() to throw exception | ||
final String MOCK_MSG = "mock: deliberate failure for parseBigInteger"; | ||
mocked.when(() -> NumberInput.parseBigInteger(Mockito.anyString())) | ||
.thenThrow(new IllegalStateException(MOCK_MSG)); | ||
mocked.when(() -> NumberInput.parseBigInteger(Mockito.anyString(), Mockito.anyBoolean())) | ||
.thenThrow(new IllegalStateException(MOCK_MSG)); | ||
|
||
// Then start testing! | ||
StringBuilder stringBuilder = new StringBuilder(); | ||
for (int i = 0; i < 999; i++) { | ||
stringBuilder.append(7); | ||
} | ||
final String testBigInteger = stringBuilder.toString(); | ||
final String json = genJson(testBigInteger); | ||
ExtractFieldsNoDefaultConstructor3730 ef = | ||
MAPPER.readValue(json, ExtractFieldsNoDefaultConstructor3730.class); | ||
assertNotNull(ef); | ||
// Ok but then let's ensure method IS called, if field is actually mapped, | ||
// first to Number | ||
try { | ||
Object ob = STRICT_MAPPER.readValue(json, UnwrappedWithNumber.class); | ||
fail("Should throw exception with mocking: instead got: "+MAPPER.writeValueAsString(ob)); | ||
} catch (DatabindException e) { | ||
verifyMockException(e, MOCK_MSG); | ||
} | ||
} | ||
} | ||
|
||
public void testIgnoreFPValuesDefault() throws Exception | ||
{ | ||
final String MOCK_MSG = "mock: deliberate failure for parseDouble"; | ||
// With default settings we would parse Doubles, so check | ||
mockStatic(NumberInput.class); | ||
when(NumberInput.parseDouble(Mockito.anyString())) | ||
try (MockedStatic<NumberInput> mocked = mockStatic(NumberInput.class)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are scoping the static mocking within try-with resources |
||
// Set up, mock NumberInput.parseDouble() to throw exception | ||
final String MOCK_MSG = "mock: deliberate failure for parseDouble"; | ||
// With default settings we would parse Doubles, so check | ||
mocked.when(() -> NumberInput.parseDouble(Mockito.anyString())) | ||
.thenThrow(new IllegalStateException(MOCK_MSG)); | ||
when(NumberInput.parseDouble(Mockito.anyString(), Mockito.anyBoolean())) | ||
mocked.when(() -> NumberInput.parseDouble(Mockito.anyString(), Mockito.anyBoolean())) | ||
.thenThrow(new IllegalStateException(MOCK_MSG)); | ||
final String json = genJson("0.25"); | ||
ExtractFieldsNoDefaultConstructor3730 ef = | ||
MAPPER.readValue(json, ExtractFieldsNoDefaultConstructor3730.class); | ||
assertNotNull(ef); | ||
|
||
// Ok but then let's ensure method IS called, if field is actually mapped | ||
// First, to "Number" | ||
try { | ||
STRICT_MAPPER.readValue(json, UnwrappedWithNumber.class); | ||
fail("Should throw exception with mocking!"); | ||
} catch (DatabindException e) { | ||
verifyMockException(e, MOCK_MSG); | ||
} | ||
|
||
// And then to "double" | ||
// 01-Feb-2023, tatu: Not quite working, yet: | ||
try { | ||
STRICT_MAPPER.readValue(json, UnwrappedWithDouble.class); | ||
fail("Should throw exception with mocking!"); | ||
} catch (DatabindException e) { | ||
e.printStackTrace(); | ||
verifyMockException(e, MOCK_MSG); | ||
// Then start testing! | ||
final String json = genJson("0.25"); | ||
ExtractFieldsNoDefaultConstructor3730 ef = | ||
MAPPER.readValue(json, ExtractFieldsNoDefaultConstructor3730.class); | ||
assertNotNull(ef); | ||
|
||
// Ok but then let's ensure method IS called, if field is actually mapped | ||
// First, to "Number" | ||
try { | ||
STRICT_MAPPER.readValue(json, UnwrappedWithNumber.class); | ||
fail("Should throw exception with mocking!"); | ||
} catch (DatabindException e) { | ||
verifyMockException(e, MOCK_MSG); | ||
} | ||
|
||
// And then to "double" | ||
// 01-Feb-2023, tatu: Not quite working, yet: | ||
try { | ||
STRICT_MAPPER.readValue(json, UnwrappedWithDouble.class); | ||
fail("Should throw exception with mocking!"); | ||
} catch (DatabindException e) { | ||
e.printStackTrace(); | ||
verifyMockException(e, MOCK_MSG); | ||
} | ||
} | ||
} | ||
|
||
public void testIgnoreFPValuesBigDecimal() throws Exception | ||
{ | ||
final String MOCK_MSG = "mock: deliberate failure for parseBigDecimal"; | ||
ObjectReader reader = MAPPER | ||
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS) | ||
.readerFor(ExtractFieldsNoDefaultConstructor3730.class); | ||
|
||
// Now should get calls to `parseBigDecimal`... eventually | ||
mockStatic(NumberInput.class); | ||
when(NumberInput.parseBigDecimal(Mockito.anyString())) | ||
try (MockedStatic<NumberInput> mock = mockStatic(NumberInput.class)) { | ||
// Set up, mock NumberInput.parseBigDecimal() to throw exception | ||
// Now should get calls to `parseBigDecimal`... eventually | ||
final String MOCK_MSG = "mock: deliberate failure for parseBigDecimal"; | ||
// With default settings we would parse Doubles, so check | ||
mock.when(() -> NumberInput.parseBigDecimal(Mockito.anyString())) | ||
.thenThrow(new IllegalStateException(MOCK_MSG)); | ||
when(NumberInput.parseBigDecimal(Mockito.anyString(), Mockito.anyBoolean())) | ||
mock.when(() -> NumberInput.parseBigDecimal(Mockito.anyString(), Mockito.anyBoolean())) | ||
.thenThrow(new IllegalStateException(MOCK_MSG)); | ||
|
||
final String json = genJson("0.25"); | ||
ExtractFieldsNoDefaultConstructor3730 ef = | ||
reader.readValue(genJson(json)); | ||
assertNotNull(ef); | ||
|
||
// But then ensure we'll fail with unknown (except does it work with unwrapped?) | ||
reader = reader.with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); | ||
|
||
// Ok but then let's ensure method IS called, if field is actually mapped | ||
// First to Number | ||
try { | ||
reader.forType(UnwrappedWithNumber.class).readValue(json); | ||
fail("Should throw exception with mocking!"); | ||
} catch (DatabindException e) { | ||
verifyMockException(e, MOCK_MSG); | ||
} | ||
|
||
// And then to "BigDecimal" | ||
// 01-Feb-2023, tatu: Not quite working, yet: | ||
try { | ||
reader.forType(UnwrappedWithBigDecimal.class).readValue(json); | ||
fail("Should throw exception with mocking!"); | ||
} catch (DatabindException e) { | ||
verifyMockException(e, MOCK_MSG); | ||
// Then start testing! | ||
ObjectReader reader = MAPPER | ||
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS) | ||
.readerFor(ExtractFieldsNoDefaultConstructor3730.class); | ||
|
||
final String json = genJson("0.25"); | ||
ExtractFieldsNoDefaultConstructor3730 ef = | ||
reader.readValue(genJson(json)); | ||
assertNotNull(ef); | ||
|
||
// But then ensure we'll fail with unknown (except does it work with unwrapped?) | ||
reader = reader.with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); | ||
|
||
// Ok but then let's ensure method IS called, if field is actually mapped | ||
// First to Number | ||
try { | ||
reader.forType(UnwrappedWithNumber.class).readValue(json); | ||
fail("Should throw exception with mocking!"); | ||
} catch (DatabindException e) { | ||
verifyMockException(e, MOCK_MSG); | ||
} | ||
|
||
// And then to "BigDecimal" | ||
// 01-Feb-2023, tatu: Not quite working, yet: | ||
try { | ||
reader.forType(UnwrappedWithBigDecimal.class).readValue(json); | ||
fail("Should throw exception with mocking!"); | ||
} catch (DatabindException e) { | ||
verifyMockException(e, MOCK_MSG); | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,7 @@ | |
import static org.mockito.Mockito.*; | ||
|
||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.mockito.Mock; | ||
import org.powermock.core.classloader.annotations.PrepareForTest; | ||
import org.powermock.modules.junit4.PowerMockRunner; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
|
@@ -15,9 +12,6 @@ | |
import org.junit.Before; | ||
import org.junit.BeforeClass; | ||
|
||
@RunWith(PowerMockRunner.class) | ||
@PrepareForTest(TypeFactory.class) | ||
|
||
public class TestTypeFactoryWithClassLoader { | ||
@Mock | ||
private TypeModifier typeModifier; | ||
|
@@ -54,7 +48,7 @@ public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNull() throws Class | |
verify(spySut).classForName(any(String.class), any(Boolean.class), eq(classLoader)); | ||
Assert.assertNotNull(clazz); | ||
Assert.assertEquals(classLoader, spySut.getClassLoader()); | ||
Assert.assertEquals(typeModifier,spySut._modifiers[0]); | ||
// Assert.assertEquals(typeModifier,spySut._modifiers[0]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All assertions such as this one fail, but simply commented out since we are only after classloader. WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JooHyukKim Yes, I think we are ok wrt not checking |
||
Assert.assertEquals(null, Thread.currentThread().getContextClassLoader()); | ||
} | ||
|
||
|
@@ -66,14 +60,14 @@ public void testUsesCorrectClassLoaderWhenThreadClassLoaderIsNotNull() throws Cl | |
verify(spySut).classForName(any(String.class), any(Boolean.class), eq(classLoader)); | ||
Assert.assertNotNull(clazz); | ||
Assert.assertEquals(classLoader, spySut.getClassLoader()); | ||
Assert.assertEquals(typeModifier,spySut._modifiers[0]); | ||
// Assert.assertEquals(typeModifier,spySut._modifiers[0]); | ||
} | ||
|
||
@Test | ||
public void testCallingOnlyWithModifierGivesExpectedResults(){ | ||
TypeFactory sut = mapper.getTypeFactory().withModifier(typeModifier); | ||
Assert.assertNull(sut.getClassLoader()); | ||
Assert.assertEquals(typeModifier,sut._modifiers[0]); | ||
// Assert.assertEquals(typeModifier,sut._modifiers[0]); | ||
} | ||
|
||
@Test | ||
|
@@ -87,7 +81,7 @@ public void testCallingOnlyWithClassLoaderGivesExpectedResults(){ | |
public void testDefaultTypeFactoryNotAffectedByWithConstructors() { | ||
TypeFactory sut = mapper.getTypeFactory().withModifier(typeModifier).withClassLoader(classLoader); | ||
Assert.assertEquals(classLoader, sut.getClassLoader()); | ||
Assert.assertEquals(typeModifier,sut._modifiers[0]); | ||
// Assert.assertEquals(typeModifier,sut._modifiers[0]); | ||
Assert.assertNull(mapper.getTypeFactory().getClassLoader()); | ||
Assert.assertArrayEquals(null,mapper.getTypeFactory()._modifiers); | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phew, took a while to fix the JDK 21 failure. The problem was that
This measure seems like necessary cost of supporting a wide range of JDK versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fun fun. Yeah, things will become more challenging going forward, no doubt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JooHyukKim @cowtowncoder any reason for ByteBuddy not being scoped to
test
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh F*CK. This is bad. How did we miss this. :-(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just release note it, no need to release 2.17.1 immediately - it is easy to work around (by excluding it in your build) and most people won't even notice the extra jar - needs to be fixed but not something to worry too much about
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this need not force release of 2.17.1