-
Notifications
You must be signed in to change notification settings - Fork 5
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
DC-1055: More Junit 5 migrations #1677
Conversation
@@ -355,4 +359,31 @@ public static <T> T extractValueFromPrivateObject(Object object, String fieldNam | |||
return objectMapper.convertValue( | |||
ReflectionTestUtils.getField(object, fieldName), new TypeReference<>() {}); | |||
} | |||
|
|||
public static String loadJson(final String resourcePath) { |
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.
These methods were ported over from the JsonLoader
component, so they could be used by unit tests that don't have a spring context. Using static methods in all cases might be a better approach generally, as there doesn't seem to be a benefit to using a component for these operations.
@Autowired private JsonLoader jsonLoader; | ||
|
||
@BeforeEach | ||
void beforeEach() { |
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.
The validator tests require similar boilerplate, it would be nice if there was a way to share this code.
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.
I often find that stubs in before each methods trigger strict stubbing errors, but that seems to not be a problem here
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.
The difference here isn't that they're @BeforeEach
, it's that using the spring boot test annotations doesn't support strict stubbing, so no strictness tests are done by mockito in this test. As far as I can tell, there's no easy way to enable strict stubbing and also use WebMvcTest
. See https://github.com/spring-projects/spring-boot/issues/19383. Although I do see a SO response that could work https://stackoverflow.com/a/75388020/947286
}) | ||
@WebMvcTest | ||
@Tag(Unit.TAG) | ||
class DatasetSchemaUpdateValidatorTest { |
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.
I renamed the test so it matches the class it's testing, DatasetSchemaUpdateValidator
values.add( | ||
new SynapseDataResultModel() | ||
.filteredCount(filteredRowCount) | ||
.totalCount(totalRowCount) | ||
.rowResult(new HashMap<>())); | ||
} else { | ||
when(azureSynapsePdao.getTableTotalRowCount(any(), any(), any())).thenReturn(totalRowCount); |
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.
This change was required due to strict stubbing
}) | ||
@WebMvcTest | ||
@Tag(Unit.TAG) | ||
class IngestRequestValidatorTest { |
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.
Renamed the test to match its class, IngestRequestValidator
"Validation of asset model should fail on " + itemChecked, | ||
ex.getCauses().get(0), | ||
containsString(expectedErrorMessage)); | ||
throw ex; |
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.
The tests here used to depend on InvalidAssetException
being rethrown. Now that we're using assertThrows()
, we no longer have to do this.
@@ -12,7 +12,7 @@ | |||
|
|||
public class ValidatorTestUtils { | |||
|
|||
public static void checkValidationErrorModel(ErrorModel errorModel, String[] messageCodes) { | |||
public static void checkValidationErrorModel(ErrorModel errorModel, String... messageCodes) { |
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.
This change allows callers to pass in the list of strings directly instead of creating an array to pass in.
private SnapshotRequestAssetModel requestAssetModel; | ||
private AssetSpecification assetSpec; | ||
private WalkRelationship walkRelationship; | ||
|
||
@Autowired AssetUtils assetUtils; | ||
@SpyBean AzureSynapsePdao azureSynapsePdaoSpy; |
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.
A few tests need to spy()
this class so they can intercept calls to executeSynapseQuery()
. Instead of spying generally, a spy is created in those specific tests that need it. Adding directly mockable interface layer for query execution might be better approach but would require changes to the class being tested.
DATA_SOURCE = 'snapshotDataSourceName1', | ||
FORMAT = 'parquet') AS rows;""" | ||
.formatted(IngestUtils.formatSnapshotTableName(snapshotId, PDAO_ROW_ID_TABLE), tableId); | ||
assertThat(queryCaptor.getValue(), equalToCompressingWhiteSpace(expected)); |
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.
Sonar had a warning that this test has no assert, so I addd one that validates the generated query.
assertThat( | ||
"Step failed due to InvalidUserProjectException and should be retried", | ||
StepStatus.STEP_RESULT_FAILURE_RETRY, | ||
equalTo(result.getStepStatus())); | ||
result.getStepStatus(), |
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.
Many calls here had swapped expected and actual parameters.
|
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.
Looks reasonable to me, thanks for the cleanup 👍🏽
@Autowired private JsonLoader jsonLoader; | ||
|
||
@BeforeEach | ||
void beforeEach() { |
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.
I often find that stubs in before each methods trigger strict stubbing errors, but that seems to not be a problem here
|
||
@Autowired private MockMvc mvc; | ||
|
||
@MockBean private JobService jobService; |
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.
in general, why do these changes require adding so many mockBean annotations? How were the tests working before without relying on these mocked services and validators before? Or were they just being relied on in a different file?
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.
This was required by the shift from using SpringRunner
to ContextConfiguration
. Before, the test was creating a spring application context such that all beans that weren't marked as MockBean
in the test would be injected with real objects. This meant that the test was creating actual components for things like IamService
, DatasetService
, etc. This works but it means that a lot of code that's not being tested gets executed, and the test behavior could change due to code not being tested here.
Using ContextConfiguration
flips things around so that only the classes declared in its classes
are real objects. Every other object must be mocked, or it it will trigger spring failure when it tries to create the context for the test.
Normally this is a good thing in that the test likely needs mocked objects anyway so it can verify or stub services that the test needs. The validator tests are a special case though. In this case, the tests require a fully mocked copy of DatasetsApiController
, which ends up pulling in all the components that the controller needs.
Although now that I think about it, maybe this test could instead mock the controller itself, or use the controller's base class (DatasetsApi
) instead. 🤔
|
||
@MockBean private ConfigurationService configService; | ||
@Mock private Dataset dataset; |
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.
why switch from mockBean to mock? Is this to avoid SpringRunner?
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.
Sort of to avoid, yes. Normally a spring runner test requires @MockBean
to both mock an api and provide a bean that replaces the real spring bean (component) from being created and injected. When writing a test that uses a spring context, @MockBean
is the only way to replace a bean with a mock at object creation time.
The odd thing here is that the code wasn't using spring injection at all so this wasn't needed. It was already creating the step explicitly using new IngestFilePrimaryDataStep()
and passing the mocked objects to the constructor. So this test was creating a spring context but wasn't using Autowired
so using MockBean
here wasn't having any effect anyway.
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.
Looks good! I just have a couple questions just for my own understanding!
This PR migrates more unit tests to JUnit 5
@ContextConfiguration
to limit the number of autowired components