RetrySynchronizationManager.getContext() is null while writing Unit test cases - java

Java 17, SpringBoot application. I tried to implement a Retry logic. Functionally, the code is working perfectly, but when I'm trying to write a JUnit test for the same, it fails as RetrySynchronizationManager.getContext() is null.
Method:
#Retryable(maxAttemptsExpression = "${retry.maxRetry}",
backoff = #Backoff(delayExpression = "${retry.maxInterval}",
multiplierExpression = "${retry.delayMultiplier}", maxDelayExpression = "${retry.maxDelay}"))
public Session connect() {
retryCountConnect = RetrySynchronizationManager.getContext().getRetryCount();
Session sshSession = null;
try {
sshSession = sshSessionPool.getSession();
sshSession.connect();
return sshSession;
} catch (Exception e) {
log.error("Exception occurred during SshSftp connect, retry count:{}, error details:{}",
retryCountConnect, ExceptionUtils.getStackTrace(e));
throw new RuntimeException(e);
}
}
Test case:
class ConnectionTest {
#Mock
Session sshSession;
#Mock
SshSessionPool sshSessionPool;
#Mock
MockedStatic<BasicSshSessionPool> basicSshSessionPoolMockedStatic;
#Mock
MockedStatic<AopContext> aopContext;
Channel channel;
SshSftpConnection sshSftpConnection;
#BeforeEach
#SneakyThrows
void setUp() {
channel = Mockito.mock(ChannelExec.class);
MockitoAnnotations.openMocks(this);
basicSshSessionPoolMockedStatic.when(() -> BasicSshSessionPool.create(anyString(), anyString(), anyString(), anyInt())).thenReturn(sshSessionPool);
sshSftpConnection = spy(new SshSftpConnection("host", "username", "password", 22));
when(sshSessionPool.getSession()).thenReturn(sshSession);
when(sshSession.openChannel(anyString())).thenReturn(channel);
aopContext.when(AopContext::currentProxy).thenReturn(sshSftpConnection);
}
#AfterEach
void cleanUp() {
aopContext.close();
basicSshSessionPoolMockedStatic.close();
}
#Test
void connect() throws Exception {
doReturn(sshSession).when(sshSessionPool).getSession();
Session actual = sshSftpConnection.connect();
verify(sshSession, times(1)).connect();
assertEquals(sshSession, actual);
}
}
Error:
Cannot invoke "org.springframework.retry.RetryContext.getRetryCount()" because the return value of "org.springframework.retry.support.RetrySynchronizationManager.getContext()" is null
java.lang.NullPointerException: Cannot invoke "org.springframework.retry.RetryContext.getRetryCount()" because the return value of "org.springframework.retry.support.RetrySynchronizationManager.getContext()" is null
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Any recommendations would be appreciated

Related

Mockserver fails to read request: unknown message format

I need to test some REST client. For that purpose I'm using org.mockserver.integration.ClientAndServer
I start my server. Create some expectation. After that I mock my client. Run this client. But when server receives request I see in logs:
14:00:13.511 [MockServer-EventLog0] INFO org.mockserver.log.MockServerEventLog - received binary request:
505249202a20485454502f322e300d0a0d0a534d0d0a0d0a00000604000000000000040100000000000408000000000000ff0001
14:00:13.511 [MockServer-EventLog0] INFO org.mockserver.log.MockServerEventLog - unknown message format
505249202a20485454502f322e300d0a0d0a534d0d0a0d0a00000604000000000000040100000000000408000000000000ff0001
This is my test:
#RunWith(PowerMockRunner.class)
#PrepareForTest({NrfClient.class, SnefProperties.class})
#PowerMockIgnore({"javax.net.ssl.*"})
#TestPropertySource(locations = "classpath:test.properties")
public class NrfConnectionTest {
private String finalPath;
private UUID uuid = UUID.fromString("8d92d4ac-be0e-4016-8b2c-eff2607798e4");
private ClientAndServer mockServer;
#Before
public void startMockServer() {
mockServer = startClientAndServer(8888);
}
#After
public void stopServer() {
mockServer.stop();
}
#Test
public void NrfRegisterTest() throws Exception {
//create some expectation
new MockServerClient("127.0.0.1", 8888)
.when(HttpRequest.request()
.withMethod("PUT")
.withPath("/nnrf-nfm/v1/nf-instances/8d92d4ac-be0e-4016-8b2c-eff2607798e4"))
.respond(HttpResponse.response().withStatusCode(201));
//long preparations and mocking the NrfClient (client that actually make request)
//NrfClient is singleton, so had to mock a lot of methods.
PropertiesConfiguration config = new PropertiesConfiguration();
config.setAutoSave(false);
File file = new File("test.properties");
if (!file.exists()) {
String absolutePath = file.getAbsolutePath();
finalPath = absolutePath.substring(0, absolutePath.length() - "test.properties".length()) + "src\\test\\resources\\test.properties";
file = new File(finalPath);
}
try {
config.load(file);
config.setFile(file);
} catch(ConfigurationException e) {
LogUtils.warn(NrfConnectionTest.class, "Failed to load properties from file " + "classpath:test.properties", e);
}
SnefProperties spyProperties = PowerMockito.spy(SnefProperties.getInstance());
PowerMockito.doReturn(finalPath).when(spyProperties, "getPropertiesFilePath");
PowerMockito.doReturn(config).when(spyProperties, "getProperties");
PowerMockito.doReturn(config).when(spyProperties, "getLastUpdatedProperties");
NrfConfig nrfConfig = getNrfConfig();
NrfClient nrfClient = PowerMockito.spy(NrfClient.getInstance());
SnefAddressInfo snefAddressInfo = new SnefAddressInfo("127.0.0.1", "8080");
PowerMockito.doReturn(nrfConfig).when(nrfClient, "loadConfiguration", snefAddressInfo);
PowerMockito.doReturn(uuid).when(nrfClient, "getUuid");
Whitebox.setInternalState(SnefProperties.class, "instance", spyProperties);
nrfClient.initialize(snefAddressInfo);
//here the client makes request
nrfClient.run();
}
private NrfConfig getNrfConfig() {
NrfConfig nrfConfig = new NrfConfig();
nrfConfig.setNrfDirectConnection(true);
nrfConfig.setNrfAddress("127.0.0.1:8888");
nrfConfig.setSnefNrfService(State.ENABLED);
nrfConfig.setSmpIp("127.0.0.1");
nrfConfig.setSmpPort("8080");
return nrfConfig;
}
}
Looks like I miss some server configuration, or use it in wrong way.
Or, maybe the reason is in powermock: could it be that mockserver is incompatible with powermock or PowerMockRunner?

How to mock KafkaAdminClient

I am trying to do a unit test for the code below. I am able to test the exception block but unable to test the below block as I am getting an exception.
How can I mock or set values to the ListTopicsResult topics = client.listTopics(), so that the flow goes into the if block?
if(!topics.names.get().isEmpty()) { response = true; }
public boolean isBrokerRunning() {
boolean response = false;
Properties property = new Properties();
try(AdminClient client = KafkaAdminClient.create(property)) {
ListTopicsResult topics = client.listTopics();
if(!topics.names.get().isEmpty()) {
response = true;
}
} catch(Exception ex) {
response = false;
}
}
KafkaAdminClient.create
This is a static function call, so you need to mock static function, You can use powermockit on top of mockito to mock static functions.
see this example
use mockito-inline can do this. need some trick
#Test
public void mockCreateAdmin() {
AdminClient mock = mock(KafkaAdminClient.class);
try (MockedStatic<Admin> staticMock = mockStatic(Admin.class)) {
staticMock.when(() -> Admin.create(any(Properties.class))).thenReturn(mock);
KafkaAdminClient adminClient = (KafkaAdminClient) KafkaAdminClient.create(new Properties());
// when
// then
assertEquals(mock, adminClient);
}
}

“Wanted but not invoked; However there were other interactions with this mock” error

The unit test keeps giving me =
Wanted but not invoked: However, there were exactly 3 interactions with this mock.
All I am trying to do is, testing the timeout for a method execution - if the method takes more time, then terminate it and publish count(to understand the timed out response rate) as metric.
#Test
public void testTimeoutFunction() throws Exception {
Response response = getResponseForTest();
when(processor
.process(any(Request.class)))
.thenAnswer((Answer<Response>) invocation -> {
Thread.sleep(100);
return response;
});
when(itemRequest.getRequestContext()).thenReturn(itemRequestContext);
testClass = spy(new TestClass(processor, executorService));
List<Item> output = testClass.getItemList(ID, itemRequest);
verify(testClass, times(1)).responseTimedOutCount();
assertTrue(output.isEmpty());
verify(testClass, timeout(EXECUTION_TIMEOUT)).buildResponse(itemRequest);
verify(testClass, times(1)).buildResponse(itemRequest);
}
This is method which I am testing for:
public class TestClass {
#VisibleForTesting
void responseTimedOutCount() {
//log metrics
}
private CompletableFuture<Response> getResponseAsync(final ScheduledExecutorService delayer,
final ItemRequest itemRequest) {
return timeoutWithTimeoutFunction(delayer, EXECUTION_TIMEOUT, TimeUnit.MILLISECONDS,
CompletableFuture.supplyAsync(() -> getResponseWithTimeoutFunction(itemRequest), executorService),
Response.emptyResponse(), () -> responseTimedOutCount());
}
private Response getResponseWithTimeoutFunction(final ItemRequest itemRequest) {
//do something and return response
}
public List<Item> getItemList(final String id, final ItemRequest itemRequest) throws Exception {
final ScheduledExecutorService delayer = Executors.newScheduledThreadPool(1);
Response response;
if(validateItemId(id){
try {
response = getResponseAsync(delayer, itemRequest).get();
} catch (final Throwable t) {
response = Response.emptyResponse();
} finally {
delayer.shutdown();
}
return transform(response, id).getItems();
} else {
return null;
}
}
}
Exception from Junit :
For this assert -
verify(testClass, times(1)).responseTimedOutCount();
Wanted but not invoked:
testClass.responseTimedOutCount();
However, there were exactly 3 interactions with this mock:
testClass.getItemList(ID, itemRequest);
testClass.validateItemId(ID);
testClass.getResponseWithTimeoutFunction(itemRequest);

Expected exception and expected message is not working in loops

Hi All I am doing Junit testing on Spring MVC project. Here the following code
Method to be tested
public UserDetails getUserInfo(String userID) {
Session session = sessionFactory.getCurrentSession();
UserDetails userDetails = new UserDetails();
Query query = null;
try {
query = session.createQuery("From UserDetails where user_Id=:userID").setParameter("userID", userID);
List < UserDetails > list = query.list();
if (CollectionUtils.isNotEmpty(list)) {
userDetails = list.get(0);
} else {
throw new RuntimeException("No identifier found on our records! for '" + userID + "'");
}
} catch (Exception e) {
throw e;
}
return userDetails; }
I am testing it for both positive and negative cases.
Here is my negative testcase
#Autowired
DaoLayer layer;
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
#Transactional
public void getUserInfoNegative() throws Exception
{
String[] inputs={"W12348","ABCDEF","123456"};
for(int i=0;i<inputs.length;i++)
{
System.out.println("/****** Invoking getUserInfo with Input "+inputs[i]+" *********/");
String msg="No identifier found on our records! for '"+inputs[i]+"'";
thrown.expect(RuntimeException.class);
thrown.expectMessage(msg);
layer.getUserInfo(input);
}
}
Here I am trying to input wrong userID's and expecting runtime exception to be thrown. The code works fine it throws the expection along with the message. But the issue is it is invoking only one time for the first input, other input values are not executed. How Can I make it to work in loop??
Note: Junit testcase passed and shows green bar.
I have altered the code but that too doesnot work for loop. Where I am doing wrong??
#Test
#Transactional
public void getUserInfoNegative() throws Exception
{
String[] inputs={"W12348","ABCDEF","123456"};
for(int i=0;i<inputs.length;i++)
{
System.out.println("/****** Invoking getUserInfo with Input "+inputs[i]+" *********/");
String msg="No identifier found on our records! for '"+inputs[i]+"'";
getUser(msg,inputs[i]);
}
}
public void getUser(String msg,String input)
{
thrown.expect(RuntimeException.class);
thrown.expectMessage(msg);
layer.getUserInfo(input);
}

How to test method Jsoup.connect (static) using PowerMock?

This is my code:
public void analyze(String url) throws SiteBusinessException {
Document doc = null;
Response response = null;
try {
response = Jsoup.connect(url).execute();
doc = Jsoup.connect(url).get();
} catch (IOException e) {
LOGGER.warn("Cannot analyze site [url={}, statusCode={}, statusMessage={} ]", new Object[] {url, response.statusCode(), response.statusMessage()});
throw new SiteBusinessException(response.statusMessage(), String.valueOf(response.statusCode()));
}
}
How can I test this method using PowerMock? I want to write test to check that when invoke .execute() then throw IOException and it catch then throw SiteBusinessException.
My code of test.
#RunWith(PowerMockRunner.class)
#PrepareForTest({Jsoup.class})
Test(expected = SiteBusinessException.class)
public void shouldThrowIOException() throws Exception {
Connection connection = PowerMockito.mock(Connection.class);
Response response = PowerMockito.mock(Response.class);
PowerMockito.when(connection.execute()).thenReturn(response);
PowerMockito.mockStatic(Jsoup.class);
expect(Jsoup.connect(SITE_URL)).andReturn(connection);
replay(Jsoup.class);
PowerMockito.when(Jsoup.connect(SITE_URL).execute()).thenThrow(new IOException());
AnalyzerService sut = new AnalyzerServiceImpl();
sut.analyzeSite(SITE_URL);
}
I got
java.lang.Exception: Unexpected exception, expected<com.siteraport.exception.SiteBusinessException> but was<java.lang.IllegalStateException>
??
You need to create a static mock of the Jsoup class. Once you have created such a mock in your test case, you can code your expectations using it.
Please see mock static method using PowerMockito documentation.
Here the Testcase using Mockito and PowerMockito:
I was able to mock the execute method using Mockito + Powermockito (you are using both EasyMock and Mockito?) The code in the test case looks as below:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Jsoup.class})
public class MyClassTest {
#Test(expected = SiteBusinessException.class)
public void shouldThrowIOException() throws Exception {
String SITE_URL = "some_url_string";
Connection connection = Mockito.mock(Connection.class);
Mockito.when(connection.execute()).thenThrow(new IOException("test"));
PowerMockito.mockStatic(Jsoup.class);
PowerMockito.when(Jsoup.connect(Mockito.anyString())).
thenReturn(connection);
AnalyzerService sut = new AnalyzerService();
sut.analyze(SITE_URL);
}
}

Categories