Skip to content
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

Merged
merged 23 commits into from
May 7, 2024
Merged

DC-1055: More Junit 5 migrations #1677

merged 23 commits into from
May 7, 2024

Conversation

pshapiro4broad
Copy link
Member

@pshapiro4broad pshapiro4broad commented May 6, 2024

This PR migrates more unit tests to JUnit 5

  • migrate all unit tests except for embedded database tests
  • where possible, tests were changed to not use a spring context
  • spring context tests were changed to use @ContextConfiguration to limit the number of autowired components

@@ -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) {
Copy link
Member Author

@pshapiro4broad pshapiro4broad May 6, 2024

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() {
Copy link
Member Author

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.

Copy link
Contributor

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

Copy link
Member Author

@pshapiro4broad pshapiro4broad May 6, 2024

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 {
Copy link
Member Author

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);
Copy link
Member Author

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 {
Copy link
Member Author

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;
Copy link
Member Author

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) {
Copy link
Member Author

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;
Copy link
Member Author

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));
Copy link
Member Author

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(),
Copy link
Member Author

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.

@pshapiro4broad pshapiro4broad changed the title More Junit 5 migrations DC-1055: More Junit 5 migrations May 6, 2024
@pshapiro4broad pshapiro4broad marked this pull request as ready for review May 6, 2024 15:18
@pshapiro4broad pshapiro4broad requested review from a team as code owners May 6, 2024 15:18
@pshapiro4broad pshapiro4broad requested review from rushtong and okotsopoulos and removed request for a team May 6, 2024 15:18
Copy link

sonarqubecloud bot commented May 6, 2024

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarCloud

Copy link
Contributor

@rushtong rushtong left a 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() {
Copy link
Contributor

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;
Copy link
Contributor

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?

Copy link
Member Author

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;
Copy link
Contributor

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?

Copy link
Member Author

@pshapiro4broad pshapiro4broad May 7, 2024

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.

Copy link
Contributor

@rjohanek rjohanek left a 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!

@pshapiro4broad pshapiro4broad merged commit b9ff77b into develop May 7, 2024
13 checks passed
@pshapiro4broad pshapiro4broad deleted the ps/unit-tests branch May 7, 2024 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants