I have a method implementation that calls a functional interface (Java 8) and I am trying to write a unit test for it. Here is the method I want to test:
// Impl
public Optional<ExternalRelease> getStagedRelease(AccessParams accessParams) throws DigitalIngestionException {
String resourcesPath=getReleaseResourcesPath(accessParams).orElseThrow(() -> new ValidationException(WRONG_ACCESS_CONTEXT_EXCEPTION));
String ddexFilePath=getReleaseDdexPath(accessParams).get();
if( stageDataManager.directoryExists(resourcesPath) ) {
List<Track> tracks = getTracks(accessParams).
orElse(new ArrayList<>());
ExternalRelease externalRelease = null;
//Verify if lite XML already exists
if( stageDataManager.fileExists(ddexFilePath) ) {
//Load externalRelease values
String liteDdex = stageDataManager.loadFileContent(ddexFilePath).
orElseThrow(() -> new ProcessException("Lite DDEX content couldn't be read."));
externalRelease = ddexManagerExecutor( (ddexManager) -> ddexManager.getExternalReleaseFromLiteDdex(liteDdex) ).
orElseThrow(() -> new ProcessException("External release couldn't be parsed from Lite DDEX."));
externalRelease.setTracks(tracks);
return Optional.ofNullable(externalRelease);
} else {
//Create lite ddex if it doesn't exist
ExternalRelease releaseFromTracks=getReleaseFromTracks(tracks);
String liteDdex = ddexManagerExecutor( (ddexManager) -> ddexManager.getLiteDdexFromAccessParamsAndExternalRelease(accessParams, releaseFromTracks)).
orElseThrow(() -> new ProcessException("Lite DDEX content couldn't be generated."));
stageDataManager.writeStringInFile(ddexFilePath, liteDdex);
return Optional.ofNullable(releaseFromTracks);
}
} else {
return Optional.empty();
}
}
Note the line within the second IF
externalRelease = ddexManagerExecutor( (ddexManager) -> ddexManager.getExternalReleaseFromLiteDdex(liteDdex) ).orElseThrow(() -> new ProcessException("External release couldn't be parsed from Lite DDEX."));`
The ddexManagerExecutor is a functional interface using java 8 features:
public interface ddexManagerConsumer<R> {
R process(DdexManager ddexManager) throws ProcessException;
}
private <R> R ddexManagerExecutor(ddexManagerConsumer<R> action) throws ProcessException {
DdexManager ddexManager = null;
try {
ddexManager = (DdexManager) ddexManagerPool.getTarget();
return action.process(ddexManager);
} catch (Exception e) {
throw new ProcessException("Error while accessing to ddexManager pool.");
} finally {
try {
ddexManagerPool.releaseTarget(ddexManager);
} catch (Exception e) {
log.error("Error while releasing ddexManager instance.", e);
}
}
}
So in my unit tests, which I am trying to do the following,
#Test
public void getStagedReleaseTest_whenFileDoesNoExist() throws Exception{
AccessParams accessParams = getDefaultAccessParams();
File file = new File("src/test/resources/testTrack.mp3");
String xml="<xml/>";
accessParams.setFileName("testTrack.mp3");
accessParams.setReleaseSlug("test-release-slug");
Optional<List<File>> mockFileList = Optional.of(Arrays.asList(file));
when(mockedStageDataManager.directoryExists(any())).thenReturn(true);
when(mockedStageDataManager.getResources(anyString(), any())).thenReturn(mockFileList);
when(mockedStageDataManager.fileExists(anyString())).thenReturn(false);
when(mockedStageDataManager.loadFileContent(anyString())).thenReturn(Optional.of(xml));
when(mockedDdexManager.getLiteDdexFromAccessParamsAndExternalRelease(any(), any())).thenReturn(Optional.of(xml));
DigitalIngestionServiceImpl serviceSpy = spy(service);
Optional<ExternalRelease> externalRelease = serviceSpy.getStagedRelease(accessParams);
Assert.assertNotNull(externalRelease);
}
This breaks my unit test since I am not mocking the call for the private functional interface. I am simply accounting for mocking the following inner line:
ddexManager.getExternalReleaseFromLiteDdex(liteDdex)
But none for the functional interface call that calls the above method. Any clue how to achieve that ?
You can mock ddexManagerPool.getTarget() in order to return a mocked DdexManager that will return what you want when calling getExternalReleaseFromLiteDdex
Related
I am developing a custom Spring cloud gateway predicate factory that will take decision based on some value in the request body.
I have written the following code.
public class OperatorIdPredicateFactory extends AbstractRoutePredicateFactory<OperatorIdPredicateFactory.Config> {
public OperatorIdPredicateFactory(Class<Config> configClass) {
super(configClass);
}
#Override
public Predicate<ServerWebExchange> apply(Config config) {
return (ServerWebExchange webExchange) -> {
return webExchange.getRequest().getBody().as((Flux<DataBuffer> body) ->{
var returnValue = new Object() {boolean value = false;}; // wrapper anonymous class for return value;
body.subscribe((DataBuffer buffer) ->{
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
String requestJson = charBuffer.toString();
ObjectMapper mapper = new ObjectMapper();
RouteRequestPayload requestPayload = null;
try {
requestPayload = mapper.readValue(requestJson, RouteRequestPayload.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
if(config.getPtoId().equalsIgnoreCase(requestPayload.getRouteRequest().getOperatorId()))
returnValue.value = true;
});
return returnValue.value;
});
};
}
// config class
}
But the problem is that the method is returning before the subscribe method finishes.
How can I make sure that the subscribe method finished before returning the value.
Here is my function for which I wrote a mockito test case which returns the list of github repositores matching a given topic using: https://javadoc.io/doc/org.eclipse.mylyn.github/org.eclipse.egit.github.core/2.1.5/index.html
githubService is an object of the class GithubService that contains the method getReposByTopics.
public CompletionStage<Result> getReposByTopics(String topic_name) {
CompletionStage<Result> results = cache
.getOrElseUpdate((topic_name + ".getReposByTopics"),
() -> githubService.getReposByTopics(topic_name)
.thenApplyAsync(repository -> ok(views.html.topics.render(repository))));
return results;
}
The Test case is:
#Test
public void getReposByTopicsTest()
{
running(provideApplication(), () -> {
when(cache.getOrElseUpdate(any(),any())).thenReturn(searchedRepositoriesObject());
CompletionStage<Result> repositories = githubController.getReposByTopics("mocktopic");
assertTrue(repositories.toCompletableFuture().isDone());
});
}
/**
*
* mock object for testing getReposByTopicsTest
* #author Trusha Patel
* #return CompletionStage<Object> represents the async response containing the process stage of SearchedRepository
*/
private CompletionStage<Object> searchedRepositoriesObject(){
return CompletableFuture.supplyAsync(() -> {
SearchedRepositoryDetails searchedRepositoryDetails = new SearchedRepositoryDetails();
List<SearchRepository> searchItem = new ArrayList<>();
SearchRepository searchMock1 = mock(SearchRepository.class);
when(searchMock1.getOwner()).thenReturn("user1");
when(searchMock1.getName()).thenReturn("repo1");
SearchRepository searchMock2 = mock(SearchRepository.class);
when(searchMock2.getOwner()).thenReturn("user2");
when(searchMock2.getName()).thenReturn("repo2");
searchItem.add(searchMock1);
searchItem.add(searchMock2);
searchedRepositoryDetails.setRepos(searchItem);
return searchedRepositoryDetails;
});
}
}
My jacoco test coverage never covers the lambda..
The same happens with the other function where instead of mocking the cache, i mock the function:
public CompletionStage<Result> getRepositoryDetails(String userName, String repositoryName) {
CompletionStage<Result> results = cache
.getOrElseUpdate((userName +"."+repositoryName),
() -> githubService.getRepositoryDetails(userName, repositoryName)
.thenApplyAsync(repository -> ok(views.html.repository.render(repository))));
return results;
}
Test case:
public void getRepositoryDetailsTest()
{
running(provideApplication(), () -> {
when(githubService.getRepositoryDetails(anyString(),anyString())).thenReturn(repositoryDetails());
CompletionStage<Result> repositoryDetails = githubController.getRepositoryDetails("play", "play");
try {
Result result = repositoryDetails.toCompletableFuture().get();
assertEquals(HttpStatus.SC_OK,result.status());
assertTrue(contentAsString(result).contains("MockRepoName"));
assertEquals("text/html",result.contentType().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
}
private CompletionStage<RepositoryDetails> repositoryDetails(){
return CompletableFuture.supplyAsync( () -> {
RepositoryDetails repositoryDetails = new RepositoryDetails();
Repository repository = new Repository();
repository.setName("MockRepoName");
repositoryDetails.setRepository(repository);
return repositoryDetails;
});
}
I have this complicated method. I want to mock just the result. All what is inside should basically be ignored. I am using Mockito .
class JiraManager
{
public static List<Issue> searchMitarbeiterIssue(String mitarbeiterName) throws JqlParseException, SearchException {
ApplicationUser user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();
JqlQueryParser jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser.class);
SearchService searchService = ComponentAccessor.getComponent(SearchService.class);
String jqlSearchString = "project = BLUB AND issuetype = BLOB AND text ~ \"" + myName+ "\""+" AND status = aktive";
final Query query = jqlQueryParser.parseQuery(jqlSearchString);
List<Issue> issues = null;
Query queryToExecute = JqlQueryBuilder.newBuilder(query).buildQuery();
// get results for the jql query
SearchResults searchResult = searchService.search(user, queryToExecute, PagerFilter.getUnlimitedFilter());
try {
Method newGetMethod = null;
try {
newGetMethod = SearchResults.class.getMethod("getIssues");
} catch (NoSuchMethodException e) {
try {
LOGGER.warn("SearchResults.getIssues does not exist - trying to use getResults!");
newGetMethod = SearchResults.class.getMethod("getResults");
} catch (NoSuchMethodError e2) {
LOGGER.error("SearchResults.getResults does not exist!");
}
}
if (newGetMethod != null) {
issues = (List<Issue>) newGetMethod.invoke(searchResult);
} else {
LOGGER.error("ERROR NO METHOD TO GET ISSUES !");
throw new RuntimeException("ICT: SearchResults Service from JIRA NOT AVAILABLE (getIssue / getResults)");
}
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
LOGGER.error("Jql Helper can not get search result (ICT)", e);
} catch (Exception e) {
LOGGER.error("Jql Helper can not get search result - other exception (ICT)", e);
}
return issues;
}
}
I do not want Mockito to run all the Code inside the method. It should just return the List . That's all . So I tried this:
try (MockedStatic<JiraManager> utilities = Mockito.mockStatic(JiraManager.class)) {
utilities.when(() -> JiraManager.searchMitarbeiterIssue(any()))
.thenReturn(Collections.singletonList(mitarbeiterIssueResult));
assertTrue(JiraManager.searchMitarbeiterIssue("").size() == 1);
}
But it is not working. It always returns null. Why ?? Is the code inside the method executed ? What is Mockito exactly doing ?
Below works for me.
Create MockedStatic class field
private MockedStatic<MyUtilClassWithStaticMethods> myUtil;
Initialise MockedStatic before each test case
#BeforeEach
void initialiseWorker() {
myUtil = mockStatic(MyUtilClassWithStaticMethods.class);
}
Close MockedStatic after each test case
#AfterEach
public void close() {
myUtil.close();
}
Mock static method behaviour in test case
#Test
void test() {
when(MyUtilClassWithStaticMethods.staticMethod(any())).thenReturn(null);
}
You can return list instead of null here.
Currently I'm dealing with a TarArchiveInputStream as this :
private Mono<Employee> createEmployeeFromArchiveFile() {
return Mono.fromCallable(() -> {
return new Employee();
})
.flatMap(employee -> {
try {
TarArchiveInputStream tar =
new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(new File("/tmp/myarchive.tar.gz"))));
TarArchiveEntry entry;
tar.read();
while ((entry = tar.getNextTarEntry()) != null) {
if (entry.getName().equals("data1.txt")) {
// process data
String data1 = IOUtils.toString(tar, String.valueOf(StandardCharsets.UTF_8));
if (data1.contains("age")) {
employee.setAge(4);
} else {
return Mono.error(new Exception("Missing age"));
}
}
if (entry.getName().equals("data2.txt")) {
// a lot more processing => put that in another function for clarity purpose
String data2 = IOUtils.toString(tar, String.valueOf(StandardCharsets.UTF_8));
employee = muchProcessing(employee, data2);
}
}
tar.close();
} catch (Exception e) {
return Mono.error(new Exception("Error while streaming archive"));
}
return Mono.just(employee);
});
}
private Employee muchProcessing(Employee employee, String data2) {
if (data2.contains("name")) {
employee.setName(4);
} else {
// return an error ?
}
return employee;
}
Firstly, is this a correct way to process the archive file with Reactor ? It works fine, but it seems like synchronous business inside a flatMap. I haven't found a better way.
Secondly, I don't know how to handle the function muchProcessing(tar). If that function triggers errors, how would it return them in order to be dealt appropriately as a Mono.error ? Since I want this function to return me an employee.
Thanks!
You can handle the task inside the flatMap as a CompletableFuture and convert it to a Mono. Here's a link on how to do that:
How to create a Mono from a completableFuture
Then, you can abstract it out as:
.flatMap(this::processEmployee).doOnError(this::logError).onErrorResume(getFallbackEmployee())
I am writing a JUnit for a method that uses FileInputStream and in the constructor only the file name is passed. The file is created as part of a servlet request and this file is not stored any where.
I am trying to Mock FileInputStream using PowerMockito so that it gives me a mocked file object. Unfortunately I get FileNotFoundException which is valid but I am not sure how to test this method then because the file doesn't exist.
Method under test:
public String viewReport() throws Exception {
this.inputStream = new FileInputStream(DOCUSIGN_REPORT_FILE);
try {
boolean returnReport = validateRequest();
if (returnReport) {
intgList = this.generateViewIntegrationReportData(getESignUIConfig());
this.createCSVFile(intgList, new FileWriter(DOCUSIGN_REPORT_FILE));
} else {
failureResponse(msgs, 400);
return null;
}
} catch (Exception e) {
e.printStackTrace();
msgs.add(new Message(ESignatureIntegrationMessageTypeEnum.MESSAGE_TYPE_ERROR,
UiIntegrationKeyConstants.UI_INTEGRATION_ERROR_CODE_500, UiIntegrationKeyConstants.UI_INTEGRATION_ERROR_TEXT_SERVICE_ERROR));
failureResponse(msgs, 500);
return null;
}
return UiIntegrationKeyConstants.REPORT_REPSONSE;
}
JUnit test so far.
#Test
public void testViewReport() throws Exception {
Map<String, Object> actionMap = new HashMap<>();
actionMap.put("application", "ESignatureIntegrationAction");
ActionContext.setContext(new ActionContext(actionMap));
FileInputStream inputStream = Mockito.mock(FileInputStream.class);
PowerMockito.whenNew(FileInputStream.class).withAnyArguments().thenReturn(inputStream);
action = new ESignatureIntegrationAction();
action.viewReport();
}
I get an exception when the code reaches to new FileInputStream(DOCUSIGN_REPORT_FILE);
Thanks for the help.
I would suggest to refactor your code in a way that allows testing without a mocking framework.
It could look somewhat like this:
public class YourClass {
// ...
public String viewReport() {
try {
boolean isValidRequest = validateRequest();
if (isValidRequest) {
IntegrationReportCsvFileHandler fileHandler = new IntegrationReportCsvFileHandler();
IntegrationReportData inputData = fileHandler.readData(new FileInputStream(DOCUSIGN_REPORT_FILE));
IntegrationReportGenerator generator = new IntegrationReportGenerator();
IntegrationReportData outputData = generator.processData(inputData, getESignUIConfig());
fileHandler.writeReport(outputData, new FileWriter(DOCUSIGN_REPORT_FILE));
} else {
failureResponse(msgs, 400);
return UiIntegrationKeyConstants.FAILURE_RESPONSE;
}
} catch (Exception e) {
e.printStackTrace();
msgs.add(new Message(ESignatureIntegrationMessageTypeEnum.MESSAGE_TYPE_ERROR,
UiIntegrationKeyConstants.UI_INTEGRATION_ERROR_CODE_500, UiIntegrationKeyConstants.UI_INTEGRATION_ERROR_TEXT_SERVICE_ERROR));
failureResponse(msgs, 500);
return UiIntegrationKeyConstants.FAILURE_RESPONSE;
}
return UiIntegrationKeyConstants.REPORT_RESPONSE;
}
// ...
}
public class IntegrationReportData {
// your custom data structure
// may as well just be a List<Data>
// may be different for input and output
}
public class IntegrationReportException extends Exception {
// your custom exception
public IntegrationReportException(String message) { super(exception); }
}
public class IntegrationReportGenerator {
public IntegrationReportData processData(IntegrationReportData data, ESignConfig config) throws IntegrationReportException {
// here's your logic that requires testing
}
}
public class IntegrationReportCsvFileHandler {
public IntegrationReportData readData(InputStream input) throws IOException {
// read data from given input stream
}
public void writeData(IntegrationReportData data, OutputStreamWriter outputWriter) throws IOException {
// write data to given output stream
}
}
That way the IntegrationReportGenerator would be easily testable.