I'm trying to use flapdoodle embedded mongodb for writing the unit tests. But I was receiving the following error. "java.lang.NoSuchMethodErrror:'com.mongodb.connection.ConnectionPoolSettings$Builder
com.mongodb.connection.ConnectionPoolSettings$Builder.maxWaitQueueSize(int)"
Is there any dependency I need to add here ? Is the code which I have written in #Before block correct. At the end I need MongoClient and MongoDatabse to pass to other method and test.
**MongoDBConfig file:**
import java.io.IOException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
#EnableMongoRepositories
public class MongoDBConfig {
#Value("${mongo.db.url}")
private String MONGO_DB_URL;
#Value(("${mongo.db.port:27017}"))
private int MONGO_DB_PORT;
#Value("${mongo.db.name}")
private String MONGO_DB_NAME;
#Bean
public MongoTemplate mongoTemplate() throws IOException {
MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017");
MongoTemplate mongoTemplate = new MongoTemplate(mongoClient, "test");
return mongoTemplate;
}
}
**UnitTestFile:**
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
import org.json.simple.parser.ParseException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.sap.crm.ana.gatewayservice.AnaGatewayServiceApplication;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodStarter;
import de.flapdoodle.embed.mongo.config.IMongodConfig;
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
import de.flapdoodle.embed.mongo.config.Net;
import de.flapdoodle.embed.mongo.distribution.Version;
import de.flapdoodle.embed.process.runtime.Network;
#SpringBootTest
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { MongoDBConfig.class })
#TestPropertySource(locations = "classpath:application-test.properties")
public class CSNHandlerTestEmbeddedMongoDB {
private CSNHandler handler;
private MongoClient mongoClient;
private MongoDatabase database;
private String model;
private MongodExecutable mongodExecutable;
private MongoTemplate mongoTemplate;
#Before
public void setup() throws Exception {
MongodStarter starter = MongodStarter.getDefaultInstance();
String bindIp = "localhost";
int port = 27017;
IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
.net(new Net(bindIp, port, Network.localhostIsIPv6())).build();
this.mongoClient = new MongoClient(bindIp, port);
this.database = mongoClient.getDatabase("test");
mongodExecutable = null;
try {
mongodExecutable = starter.prepare(mongodConfig);
mongodExecutable.start();
} catch (Exception e) {
// log exception here
if (mongodExecutable != null)
mongodExecutable.stop();
}
}
#Test
public void testCSNHandler() throws IOException, ParseException {
handler = new CSNHandler(database);
handler.insertUpdateAnaModel("100", model);
}
#After
public void teardown() throws Exception {
if (mongodExecutable != null)
mongodExecutable.stop();
}
}
**Dependencies:**
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.12.9</version>
</dependency>
Related
So Im writing unit test a class that does bankId authentication and the unit test looks like this.
package se.kt.client;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpEntity;
import se.kt.common.vo.PublicApplicationForm;
import se.kt.models.BankIdAuthRequest;
import se.kt.models.BankIdAuthResponse;
import se.kt.models.BankIdCollectResponse;
import static org.mockito.ArgumentMatchers.any;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class BankIdClientTest {
private final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
#InjectMocks
private BankIdClient bankIdClient;
#BeforeEach
public void setUp() {
}
#Test
public void testBankIdAuthentication_success() throws InterruptedException {
PublicApplicationForm form = new PublicApplicationForm();
form.setSsn("123456-7890");
form.setIp_address("123.123.123.123");
BankIdAuthRequest authRequest = bankIdClient.authRequestFromApplicationForm(form, "123");
BankIdAuthResponse authResponse = new BankIdAuthResponse();
authResponse.setOrderRef("123456");
BankIdCollectResponse collectResponse = new BankIdCollectResponse();
collectResponse.setStatus("completed");
Mockito.when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), any()))
.thenReturn(ResponseEntity.ok(authResponse));
Mockito.when(restTemplate.getForEntity(anyString(), any()))
.thenReturn(ResponseEntity.ok(collectResponse));
assertTrue(bankIdClient.bankIdAuthentication(authRequest));
}
}
And the class Im testing looks like this:
package se.kt.client;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import se.kt.common.domain.AbstractApplicationForm;
import se.kt.common.vo.PublicApplicationForm;
import se.kt.models.BankIdAuthRequest;
import se.kt.models.BankIdAuthResponse;
import se.kt.models.BankIdCollectResponse;
import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.AssertTrue;
import java.util.Objects;
#Component
public class BankIdClient {
private static final Logger log = LoggerFactory.getLogger(BankIdClient.class);
private final RestTemplate customRestTemplate;
private static final String CONTENT_TYPE = "Content-Type";
#Value("${BankId.AuthUrl}")
private String bankIdAuthUrl;
#Value("${BankId.CollectUrl}")
private String bankIdCollectUrl;
#Value("${BankId.SecretKey}")
private String bankIdSecretKey;
public BankIdClient(RestTemplate customRestTemplate) {
this.customRestTemplate = customRestTemplate;
}
public BankIdAuthRequest authRequestFromApplicationForm(PublicApplicationForm form, String jobId) {
BankIdAuthRequest bankIdAuthRequest = new BankIdAuthRequest();
bankIdAuthRequest.setPno(form.getSsn());
bankIdAuthRequest.setIpAddress(form.getIp_address());
bankIdAuthRequest.setRefID(jobId);
bankIdAuthRequest.setSecretKey(bankIdSecretKey);
bankIdAuthRequest.setAvsikt("Kt application");
return bankIdAuthRequest;
}
public boolean bankIdAuthentication(BankIdAuthRequest bankIdAuthRequest) throws InterruptedException {
//Setup header and body for request.
HttpHeaders headers = new HttpHeaders();
headers.add(CONTENT_TYPE, MediaType.APPLICATION_JSON.toString());
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
try {
String bankIdAuthFormJson = ow.writeValueAsString(bankIdAuthRequest);
HttpEntity<String> httpEntity = new HttpEntity<>(bankIdAuthFormJson, headers);
ResponseEntity<BankIdAuthResponse> authResponse = customRestTemplate.postForEntity(bankIdAuthUrl, httpEntity, BankIdAuthResponse.class);
bankIdCollectUrl += Objects.requireNonNull(authResponse.getBody()).getOrderRef();
ResponseEntity<BankIdCollectResponse> collectResponse;
do {
collectResponse = customRestTemplate.getForEntity(bankIdCollectUrl, BankIdCollectResponse.class);
Thread.sleep(1500);
if (Objects.requireNonNull(collectResponse.getBody()).getStatus().equals("completed"))
return true;
if (Objects.requireNonNull(collectResponse.getBody()).getStatus().equals("failed"))
return false;
} while (Objects.requireNonNull(collectResponse.getBody()).getStatus().equals("progress"));
} catch (JsonProcessingException e) {
log.info(e.getMessage());
} catch (NullPointerException e) {
log.info(e.toString());
log.info("BankId API not responding correctly. Check server connection");
}
return false;
}
public void cancelBankIdAuthentication(#Value("${BankId.CancelUrl}") String bankIdCancelUrl) {
customRestTemplate.postForEntity(bankIdCancelUrl, null, String.class);
}
}
Now for some reson this line:
ResponseEntity<BankIdAuthResponse> authResponse = customRestTemplate.postForEntity(bankIdAuthUrl, httpEntity, BankIdAuthResponse.class);
keeps producing the result authResponse = null indicating that
Mockito.when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), any()))
.thenReturn(ResponseEntity.ok(authResponse));
is not working. Now I have been playing around with this for over 3h and i still get the same result. What could i be doing wrong?
#InjectMocks only works with #Mock-annotated fields, not with fields which get assigned a manually created mock. Therefore you need to change:
private final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
#InjectMocks
private BankIdClient bankIdClient;
to
#Mock
private RestTemplate restTemplate;
#InjectMocks
private BankIdClient bankIdClient;
I also recommend reading Why is my class not calling my mocked methods in unit test? which provides additional insights and points out some common mistakes when using Mockito or mocks in general.
I have a SpringBoot Kafka streams maven app. I use a spring-boot-starter-parent 2.4.4 for my springboot dependencies and kafka-streams 2.7.0.
I am stuck at running tests with
java.lang.NullPointerException
when trying to load my application configuration from either
resources/application.yml or test/resources/application-test.resources or test/resources/application.yml
I have a Config class with this annotations and getters and setters for fields, which are defined with same name as in the application.yml
package com.acme.rtc.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
#ConfigurationProperties(prefix = "topics")
#Component
#Configuration
public class ConfigProps {
private String MATRIXX_ADJ_EVENT_TOPIC;
private String OUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC;
private String OUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC;
private String EVENTS_NO_MVNO;
public void setMATRIXX_ADJ_EVENT_TOPIC(String MATRIXX_ADJ_EVENT_TOPIC) {
this.MATRIXX_ADJ_EVENT_TOPIC = MATRIXX_ADJ_EVENT_TOPIC;
}
public void setOUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC(String OUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC) {
this.OUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC = OUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC;
}
public void setOUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC(String OUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC) {
this.OUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC = OUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC;
}
public String getEVENTS_NO_MVNO() {
return EVENTS_NO_MVNO;
}
public void setEVENTS_NO_MVNO(String EVENTS_NO_MVNO) {
this.EVENTS_NO_MVNO = EVENTS_NO_MVNO;
}
public String getMATRIXX_ADJ_EVENT_TOPIC() {
return MATRIXX_ADJ_EVENT_TOPIC;
}
public String getOUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC() {
return OUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC;
}
public String getOUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC() {
return OUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC;
}
}
I am doing #Autowire of this class in my test and app class,
#Autowired
ConfigProps cp;
and trying to access fields using cp.getBootstrapServerHost() but this resolves to a NullPointer in my test class. But resolves properly in my application class...
My test class looks like this
package distinct;
import com.acme.rtc.configuration.ConfigProps;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.apache.kafka.streams.*;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.kafka.config.KafkaStreamsConfiguration;
import com.acme.rtc.configuration.KafkaConfiguration;
import com.acme.rtc.configuration.TopologyConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.List;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static org.junit.jupiter.api.Assertions.assertEquals;
#SpringBootTest
#ContextConfiguration(classes = TopologyConfiguration.class)
#SpringJUnitConfig
public class TestWithTopologyTestDriver {
private TestInputTopic<String, String> inputTopicWrong;
private TestOutputTopic<String, String> outputTopicWrong;
private TestInputTopic<String, String> inputTopicRight;
private TestOutputTopic<String, String> outputTopicRight;
private TopologyTestDriver topologyTestDriver;
#Autowired
ConfigProps configProps;
#BeforeEach
public void setUp() {
KafkaProperties properties = new KafkaProperties();
properties.setBootstrapServers(singletonList("localhost:9092"));
KafkaStreamsConfiguration config = new KafkaConfiguration(properties).getStreamsConfig();
StreamsBuilder sb = new StreamsBuilder();
Topology topology = new TopologyConfiguration().createTopology(sb);
topologyTestDriver = new TopologyTestDriver(topology, config.asProperties());
inputTopicWrong =
topologyTestDriver.createInputTopic(configProps.getMATRIXX_ADJ_EVENT_TOPIC(), new StringSerializer(),
new StringSerializer());
outputTopicWrong =
topologyTestDriver.createOutputTopic(configProps.getOUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC(), new StringDeserializer(),
new StringDeserializer());
inputTopicRight =
topologyTestDriver.createInputTopic(configProps.getMATRIXX_ADJ_EVENT_TOPIC(), new StringSerializer(),
new StringSerializer());
outputTopicRight =
topologyTestDriver.createOutputTopic(configProps.getOUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC(), new StringDeserializer(),
new StringDeserializer());
}
#AfterEach
public void tearDown() {
topologyTestDriver.close();
}
#Test
void wrongDistinctTopology() {
testTopology(inputTopicWrong, outputTopicWrong);
}}
where TopologyConfiguration is my application and that has this signature
package com.acme.rtc.configuration;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.serialization.Serde;
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.common.serialization.Serializer;
import org.apache.kafka.common.serialization.Deserializer;
import org.apache.kafka.streams.Topology;
import org.apache.kafka.streams.kstream.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.kafka.support.serializer.JsonDeserializer;
import org.springframework.kafka.support.serializer.JsonSerializer;
import org.springframework.stereotype.Component;
#Configuration
#ConfigurationProperties(prefix = "topics")
#Component
#RequiredArgsConstructor
public class TopologyConfiguration {
#Autowired
Environment env;
#Autowired
ConfigProps configProps;
private void acmeStreamsTopoloy(StreamsBuilder streamsBuilder) {
Deserializer<JsonNode> jsonDeserializer = new JsonDeserializer();
Serializer<JsonNode> jsonSerializer = new JsonSerializer();
Serde<JsonNode> jsonSerde = Serdes.serdeFrom(jsonSerializer, jsonDeserializer);
System.out.println("ConfigProps.getMattrix: "+configProps.getMATRIXX_ADJ_EVENT_TOPIC());
KStream<String, String> inputStream =
streamsBuilder.stream(configProps.getMATRIXX_ADJ_EVENT_TOPIC(), Consumed.with(Serdes.String(), Serdes.String()));
KStream<String, String>[] branches = inputStream.branch(
(key, value)-> value.contains("KGN"),
(key, value)-> value.contains("LEB"),
(key, value)->true);
branches[0].to(configProps.getOUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC());
branches[1].to(configProps.getOUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC());
branches[2].to(configProps.getEVENTS_NO_MVNO());
}
#Bean
public Topology createTopology(StreamsBuilder streamsBuilder) {
acmeStreamsTopoloy(streamsBuilder);
return streamsBuilder.build();
}
}
My KafkaConfiguration class
package com.acme.rtc.configuration;
import lombok.RequiredArgsConstructor;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.streams.StreamsConfig;
import org.apache.kafka.streams.errors.LogAndContinueExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.annotation.KafkaStreamsDefaultConfiguration;
import org.springframework.kafka.config.KafkaStreamsConfiguration;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.kafka.support.serializer.JsonDeserializer;
import org.springframework.kafka.support.serializer.JsonSerializer;
import java.util.HashMap;
import java.util.Map;
#Configuration
#RequiredArgsConstructor
public class KafkaConfiguration {
public static final String APP_ID = "acme-stream-rtc";
private final KafkaProperties kafkaProperties;
#Autowired
#Bean(name = KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME)
public KafkaStreamsConfiguration getStreamsConfig() {
Map<String, Object> props = new HashMap<>();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, APP_ID);
props.put(StreamsConfig.NUM_STREAM_THREADS_CONFIG, 2);
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
props.put(JsonSerializer.ADD_TYPE_INFO_HEADERS,false);
KafkaStreamsConfiguration streamsConfig = new KafkaStreamsConfiguration(props);
return streamsConfig;
}
}
My application.yml has the right syntax etc.
spring:
kafka:
bootstrap-servers: localhost:9092
json:
value:
default:
type: true
kafka:
streams:
properties:
default:
value:
serde: org.springframework.kafka.support.serializer.JsonSerde
admin:
security:
protocol: SSL
ssl:
trust-store-location: ${TRUSTSTORE_LOCATION}
trust-store-password: ${TRUSTSTORE_PASSWORD}
key-store-location: ${KEYSTORE_LOCATION}
key-store-password: ${KEYSTORE_PASSWORD}
key-password: ${KEY_PASSWORD}
topics:
MATRIXX_ADJ_EVENT_TOPIC: input-matrixx-adj-event
OUTPUT_MNVO_KGN_ADJ_EVENT_TOPIC: output-KGN-adj-event
OUTPUT_MNVO_LEB_ADJ_EVENT_TOPIC: output-LEB-adj-event
EVENTS_NO_MVNO: events-no-mvno-spec
My main class
package com.acme.rtc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.kafka.annotation.EnableKafkaStreams;
#SpringBootApplication
#EnableKafkaStreams
public class StreamProcessing {
public static void main(String[] args) {
SpringApplication.run(StreamProcessing.class, args);
}
}
I am not sure if I am missing any context when Autowiring my ConfigProps class or if I need further annotations on my test class.
For JUnit4 you need #Runwith(SpringJUnit4ClassRunner.class) alongside the #ContextConfiguration.
For JUnit5, use #SpringJUnitConfig.
For proper loading of properties, though, you need #SpringBootTest.
Boot 2.4 uses JUnit5.
And you should not have #ConfigurationProperties on the test.
EDIT
I just tested it with no problems.
#Configuration
public class Config {
#Bean
String str() {
return "str";
}
}
#ConfigurationProperties(prefix = "foo")
#Component
public class MyProps {
String bar;
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "MyProps [bar=" + this.bar + "]";
}
}
#SpringBootApplication
public class So67078244Application {
public static void main(String[] args) {
SpringApplication.run(So67078244Application.class, args);
}
}
#SpringBootTest
class So67078244ApplicationTests {
#Autowired
MyProps props;
#Test
void contextLoads() {
System.out.println(this.props);
}
}
foo.bar=baz
MyProps [bar=baz]
Can you please help me to mock SimpleJdbcCall?
the actual class to test is below.
import java.sql.Types;
import java.util.HashMap;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
public class HeartBeat {
private static final Logger LOGGER = LogManager.getLogger(HeartBeat.class);
#Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall jdbcCall;
#PostConstruct
private void applicationStarted() {
LOGGER.info("Application has Started.");
DBCall();
}
#Scheduled(fixedRateString="${application.heartbeatInterval}")
public void sendHeartbeat() {
DBCall();
}
private void DBCall() {
if ( this.jdbcTemplate == null ) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
jdbcTemplate.setResultsMapCaseInsensitive(true);
jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withSchemaName("schema")
.withCatalogName("catalogName")
.withProcedureName("cleanup");
jdbcCall.execute(new HashMap<String, Object>(0));
jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withSchemaName("schema")
.withCatalogName("catalogName")
.withProcedureName("register")
.declareParameters(new SqlParameter("id", Types.VARCHAR))
.declareParameters(new SqlParameter("name", Types.VARCHAR));
MapSqlParameterSource paramMap = new MapSqlParameterSource().addValue("id", "abc").addValue("name", "def");
jdbcCall.execute(paramMap);
}
}
to test out above code, I've created
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import java.util.HashMap;
import java.util.Map;
public class HeartBeatTest {
HeartBeat heartBeat;
private JdbcTemplate mockedJdbcTemplate;
private SimpleJdbcCall mockedJdbcCall;
private Map<String, Object> result;
#Before
public void setUp() throws Exception {
this.heartBeatComponent = new HeartBeatComponent();
result = new HashMap<String, Object>();
mockedJdbcTemplate = Mockito.mock(JdbcTemplate.class);
mockedJdbcCall = Mockito.mock(SimpleJdbcCall.class);
Whitebox.setInternalState(heartBeatComponent, "jdbcTemplate",mockedJdbcTemplate);
Whitebox.setInternalState(heartBeatComponent, "jdbcCall",mockedJdbcCall);
// section1
//PowerMockito.whenNew(SimpleJdbcCall.class).withArguments(mockedJdbcTemplate).thenReturn(mockedJdbcCall);
// section2
PowerMockito.whenNew(SimpleJdbcCall.class).withAnyArguments().thenReturn(mockedJdbcCall);
Mockito.when(mockedJdbcCall.withSchemaName(any(String.class))).thenReturn(mockedJdbcCall);
Mockito.when(mockedJdbcCall.withCatalogName(any(String.class))).thenReturn(mockedJdbcCall);
Mockito.when(mockedJdbcCall.withProcedureName(any(String.class))).thenReturn(mockedJdbcCall);
}
#Test
public void sendHeartbeatTest() {
Mockito.when(mockedJdbcCall.execute(anyMap())).thenReturn(result);
Mockito.when(mockedJdbcCall.execute(any(MapSqlParameterSource.class))).thenReturn(result);
heartBeat.sendHeartbeat();
Mockito.verify(mockedJdbcCall, Mockito.times(1)).execute(anyMap());
Mockito.verify(mockedJdbcCall, Mockito.times(1)).execute(any(MapSqlParameterSource.class));
}
}
but it does not work as expected. what I can see from debug mode,
jdbcCall got mockedJdbcCall but because of new SimpleJdbcCall() along with .withSchemaName and others, it got overwritten by simpleJdbcCall. My expectation was when new SimpleJdbcCall being called it returns mockedJdbcCall that I set up PowerMockito.whenNew...
I've tried section 1 and section 2 for PowerMockito.whenNew
Did I misunderstand PowerMockito.whenNew? or missed anything from the code?
Please, guide me.
You need to tell PowerMock that you want to mock construction of the SimpleJdbcCall class inside the HeartBeat class. Try adding the following annotations to your test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(HeartBeat.class)
public class HeartBeatTest {
...
}
Reference: how-to-mock-construction-of-new-objects
In Login class #Autowired is not null but when I try to use postman call Logout class #Autowired get null. I copy code from Login to Logout class just change variable name all code is same pattern I don't know what happening
package xxx.api.login
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import xxx.api.domain.logout.LoginRequest;
import xxx.api.domain.logout.LoginResponse;
import xxx.converter.FieldValidation;
import xxx.dto.log.MessageLog;
import xxx.logging.LogFactory;
#Component
public class LoginClient {
#Autowired
FieldValidation fieldValidation;
public LoginResponse login(LoginRequest loginRequest) throws Exception {
ObjectMapper mapper = new ObjectMapper();
final String url = "http://localhost:xxx/xxx/login";
LoginResponse responseObject = null;
try {
String requestData = mapper.writeValueAsString(loginRequest);
responseObject = login(url, requestData);
fieldValidation.validateResponse(responseObject, "login");
// 'fieldValidation' not null
This is my Login class. Return result as I expect.
package xxx.api.logout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import xxx.api.domain.logout.LogoutRequest;
import xxx.api.domain.logout.LogoutResponse;
import xxx.converter.FieldValidation;
import xxx.dto.log.MessageLog;
import xxx.logging.LogFactory;
#Component
public class LogoutClient {
#Autowired
FieldValidation fieldValidation;
public LogoutResponse logout(LogoutRequest logoutRequest) throws Exception {
ObjectMapper mapper = new ObjectMapper();
final String url = "http://localhost:xxx/xxx/logout";
LogoutResponse responseObject = null;
try {
String requestData = mapper.writeValueAsString(logoutRequest);
responseObject = logout(url, requestData);
fieldValidation.validateResponse(responseObject, "logout");
// 'fieldValidation' is null then throws nullPointerException
This is my Logout class
package xxx.api.logout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xxx.api.domain.logout.LogoutRequest;
import xxx.api.domain.logout.LogoutResponse;
import xxx.dto.log.MessageLog;
import xxx.exception.common.ErrorException;
import xxx.api.logout.LogoutClient;
import xxx.converter.FieldValidation;
#RestController
#RequestMapping(path = "/xxx")
public class LogoutServer {
#Autowired
FieldValidation fieldValidation;
#Autowired
WSLogFactory wsLog;
#PostMapping(value = "/logout", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> logout(#RequestHeader(value = "requestId") String requestId,
#RequestBody(required = true) LogoutRequest logoutRequest) throws ErrorException, Exception {
writeLogRequest(logoutRequest, logoutRequest.getClientIP(), "Request", "/xxx/logout");
fieldValidation.validateRequest(logoutRequest, "logout");
try {
LogoutClient logoutClient = new LogoutClient();
LogoutResponse response = logoutClient.logout(logoutRequest);
return new ResponseEntity<LogoutResponse>(response, HttpStatus.OK);
} catch (Exception e) {
throw e;
}
}
This is Logout Controller
#org.springframework.context.annotation.Configuration
#PropertySource("file:${layout.properties_file}")
public class FieldValidation {
// do somethings
}
This is my FieldValidation class.
{
"timestamp": 1550490230074,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.NullPointerException",
"message": "No message available",
"path": "/umm/logout"
}
This is return when I call my Logout class.
This is my project structure
xxx.api.login <<< LoginClient.java
xxx.api.logout <<< LogoutClient.java
xxx.converter <<< FieldValidation.java
This is my main program
package xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I try to replace #Component to #Configuration but still doesn't works.
I don't need 'fieldValidation' in Logout class or another class be a null value.
package xxx.converter;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Year;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.springframework.context.annotation.Configuration;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.validator.routines.BigDecimalValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import com.fasterxml.jackson.databind.ObjectMapper;
import xxx.exception.common.ErrorException;
#Configuration
#PropertySource("file:${layout.properties_file}")
public class FieldValidation {
public void validateObject(Object objResponse, String config) throws Exception {
try {
List<Configuration> confs = objectMapper.readValue(config,
objectMapper.getTypeFactory().constructCollectionType(List.class, Configuration.class));
for (int i = 0; i < confs.size(); i++) {
Configuration configuration = confs.get(i);
Object objValue = getValue(configuration.getFieldName(), objResponse);
Validation validation = new Validation();
BeanUtils.copyProperties(validation, configuration);
isValid(objValue, validation);
if (configuration.getType().equalsIgnoreCase("object")) {
List<Validation> validations = configuration.getValidation();
Class<?> act = Class.forName(configuration.getClassName());
Object objectConfig = act.cast(objValue);
validateObject(objectConfig, validations);
}
}
} catch (Exception e) {
throw e;
}
}
This is full import of FieldValidation class and replace #Configuration and I get this error.
configuration.getFieldName() << error The method is undefined for type Configuration
configuration.getType() << error The method is undefined for type Configuration
configuration.getValidation() << error The method is undefined for type Configuration
configuration.getClassName() << error The method is undefined for type Configuration
I want to confirm rollback behavior, but I'm having trouble getting it to work.
I have a postgres DB with the following tables:
select * from cat;
pkid | name
--------------------------------------+-------
c75d6e8b-6aff-4214-ad45-d17db254857b | Abbey
select * from toy;
pkid | name | description
--------------------------------------+---------------+-------------------------------------------
dda72782-a1aa-4c0e-9cf6-a408db58a1ae | Laser pointer | Red laser.
f4d7e67d-1b26-4d8d-bb98-1a5c69f3cb49 | String | Colored string attached to a plastic rod.
select * from cattoy;
pkid | fkcat | fktoy
------+-------+-------
I have created a CatService implementation with the idea being you can create a cat, toy, and associate that toy with that cat. If any one of the 3 operations fails I want them all to rollback.
DefaultCatService.java:
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.bonkeybee.dao.CatDao;
import com.bonkeybee.dao.CatToyDao;
import com.bonkeybee.dao.ToyDao;
import com.bonkeybee.querydsl.Cat;
import com.bonkeybee.querydsl.Cattoy;
import com.bonkeybee.querydsl.Toy;
#Inject
private CatDao catDao;
#Inject
private CatToyDao catToyDao;
#Inject
private ToyDao toyDao;
#Override
#Transactional
public UUID createCat(final Cat cat){
LOG.debug("Creating cat");
UUID catPkid = catDao.createCat(cat);
Toy toy = new Toy();
toy.setName("Box");
toy.setDescription("Cardboard box.");
toy.setPkid(toyDao.createToy(toy));
Cattoy catToy = new Cattoy();
catToy.setFkcat(catPkid);
catToy.setFktoy(toy.getPkid());
catToyDao.createCatToy(catToy);
return catPkid;
}
I have created DAO's and their implementations for each table with basic CRUD operations.
CatDaoJdbc.java:
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.bonkeybee.querydsl.Cat;
import com.bonkeybee.querydsl.QCat;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.SimplePath;
import com.querydsl.core.types.dsl.StringPath;
import com.querydsl.sql.SQLQueryFactory;
private static final QCat CAT = QCat.cat;
private static final SimplePath<Object> CAT_PKID = CAT.pkid;
private static final StringPath CAT_NAME = CAT.name;
#Inject
private SQLQueryFactory sqlQueryFactory;
#Override
public UUID createCat(final Cat cat) {
UUID catPkid = UUID.randomUUID();
sqlQueryFactory.insert(CAT)
.columns(CAT_PKID, CAT_NAME)
.values(catPkid, cat.getName())
.execute();
return catPkid;
}
ToyDaoJdbc.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.bonkeybee.querydsl.QToy;
import com.bonkeybee.querydsl.Toy;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.SimplePath;
import com.querydsl.core.types.dsl.StringPath;
import com.querydsl.sql.SQLQueryFactory;
private static final QToy TOY = QToy.toy;
private static final SimplePath<Object> TOY_PKID = TOY.pkid;
private static final StringPath TOY_NAME = TOY.name;
private static final StringPath TOY_DESCRIPTION = TOY.description;
#Inject
private SQLQueryFactory sqlQueryFactory;
#Override
public UUID createToy(Toy toy) {
UUID toyPkid = UUID.randomUUID();
sqlQueryFactory.insert(TOY)
.columns(TOY_PKID, TOY_NAME, TOY_DESCRIPTION)
.values(toyPkid, toy.getName(), toy.getDescription())
.execute();
return toyPkid;
}
CatToyDaoJdbc.java:
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.bonkeybee.querydsl.Cattoy;
import com.bonkeybee.querydsl.QCat;
import com.bonkeybee.querydsl.QCattoy;
import com.bonkeybee.querydsl.QToy;
import com.bonkeybee.querydsl.Toy;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.SimplePath;
import com.querydsl.sql.SQLQueryFactory;
#Override
public UUID createCatToy(Cattoy catToy) {
throw new RuntimeException("Simulating exception");
}
Main.java:
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.transaction.annotation.Transactional;
import com.bonkeybee.querydsl.Cat;
import com.bonkeybee.querydsl.Cattoy;
import com.bonkeybee.querydsl.Toy;
import com.bonkeybee.service.CatService;
import com.bonkeybee.service.CatToyService;
import com.bonkeybee.service.ToyService;
public static void main(String[] args) {
try (ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfiguration.class)) {
CatService catService = applicationContext.getBean(CatService.class);
Cat newCat = new Cat();
newCat.setName(DORA);
newCat.setPkid(catService.createCat(newCat));
}
}
ApplicationConfiguration.java:
import java.beans.PropertyVetoException;
import javax.inject.Inject;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.querydsl.sql.PostgreSQLTemplates;
import com.querydsl.sql.SQLQueryFactory;
#Configuration
#EnableTransactionManagement
#ComponentScan("com.bonkeybee")
public class ApplicationConfiguration {
#Bean
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(JDBC_DRIVER);
comboPooledDataSource.setJdbcUrl(JDBC_URL);
comboPooledDataSource.setUser(USER);
comboPooledDataSource.setPassword(PASSWORD);
comboPooledDataSource.setMinPoolSize(MIN_POOL_SIZE);
comboPooledDataSource.setInitialPoolSize(MIN_POOL_SIZE);
comboPooledDataSource.setMaxPoolSize(MAX_POOL_SIZE);
return comboPooledDataSource;
}
#Bean
#Inject
public PlatformTransactionManager getPlatformTransactionManager(final DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public com.querydsl.sql.Configuration getQueryDslConfiguration() {
return new com.querydsl.sql.Configuration(PostgreSQLTemplates.builder().build());
}
#Bean
#Inject
public SQLQueryFactory getSQLQueryFactory(final com.querydsl.sql.Configuration configuration, final DataSource dataSource) {
return new SQLQueryFactory(configuration, dataSource);
}
}
When Main calls catService.createCat() the cat and toy are created, then a RuntimeException is thrown as expected, however inspection of the tables afterward show the new cat and new toy created instead of being rolled back. Please SO, help me ensure no cat goes toyless >:3
EDIT: Adding imports as requested
Solved it after more searching, there were two configuration issues.
First: the transaction manager bean spring looks for by default should be named "transactionManager" otherwise you have to explicitly set the name.
Second: I added a dependency on "querydsl-sql-spring" artifact and changed my SQLQueryFactory to use a SpringConnectionProvider instead of the DataSource bean (found from this example from the querydsl people). Below is the final configuration:
#Configuration
#EnableTransactionManagement
#ComponentScan("com.bonkeybee")
public class ApplicationConfiguration {
#Bean
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(JDBC_DRIVER);
comboPooledDataSource.setJdbcUrl(JDBC_URL);
comboPooledDataSource.setUser(USER);
comboPooledDataSource.setPassword(PASSWORD);
comboPooledDataSource.setMinPoolSize(MIN_POOL_SIZE);
comboPooledDataSource.setInitialPoolSize(MIN_POOL_SIZE);
comboPooledDataSource.setMaxPoolSize(MAX_POOL_SIZE);
return comboPooledDataSource;
}
#Inject
#Bean(name = "transactionManager")
public PlatformTransactionManager getPlatformTransactionManager(final DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public com.querydsl.sql.Configuration getQueryDslConfiguration() {
return new com.querydsl.sql.Configuration(PostgreSQLTemplates.builder().build());
}
#Inject
#Bean
public SQLQueryFactory getSQLQueryFactory(final com.querydsl.sql.Configuration configuration, final DataSource dataSource) {
Provider<Connection> provider = new SpringConnectionProvider(dataSource);
return new SQLQueryFactory(configuration, provider);
}
}
Thanks querydsl people for such a cool lib.
Since it is more than one database request involve in your transaction, you have to specify
the persistence context as PersistenceContextType.EXTENDED, which means that it can survive multiple requests.
You have to have a entity manager and then get transaction object from it.
After getting the transactionmanager, begin a new transaction, do all your database operations and then commit. Here is a sample below
EntityManagerFactory emf = ...
EntityManager em = emf.createEntityManager (PersistenceContextType.EXTENDED);
Magazine mag1 = em.find (Magazine.class, magId);
Magazine mag2 = em.find (Magazine.class, magId);
em.getTransaction().begin();
:
:
em.getTransaction().end();