The schema creation is done inside the target class SimpleRepo.java.
public class SimpleRepo {
private Cluster cluster;
private Session session;
private String keyspace = "app";
private String table = "myTable";
#Autowired
public SimpleRepo(Cluster cluster) {
this.cluster = cluster;
}
#PostConstruct
private void init() {
session = cluster.connect();
createSchema();
}
public void createSchema() {
.....
}
}
When running the SimpleTest.java with one test case inside, it will pass. When running with two cases inside, only the first one passes and the second one throws out the exception: "com.datastax.driver.core.exceptions.InvalidQueryException: Keyspace app does not exist".
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class, SimpleRepo.class})
#TestExecutionListeners({CassandraUnitTestExecutionListener.class, DependencyInjectionTestExecutionListener.class})
#EmbeddedCassandra
public class SimpleTest {
#Autowired
private SimpleRepo simpleRepo;
#Test
public void testSave() throws Exception {
......
}
#Test
public void testDel() throws IOException {
......
}
}
#Configuration
public class TestConfig {
#Bean(destroyMethod = "shutdown")
public Cluster cluster() throws ConfigurationException, TTransportException, IOException, InterruptedException{
EmbeddedCassandraServerHelper.startEmbeddedCassandra();
Cluster cluster = Cluster.builder()
.addContactPoints("127.0.0.1")
.withPort(9142)
.build();
return cluster;
}
}
Why the keyspace created inside createSchema() would disappear when running the second test case? How to fix this problem? Thanks for any guidance.
CassandraUnitTestExecutionListener calls cleanServer() after each test. That calls EmbeddedCassandraServerHelper.cleanEmbeddedCassandra() which drops all non-system keyspaces.
Your code creates your keyspace only once, in #PostConstruct, so only 1st test case can use it.
It looks like you should use #CassandraDataSet to initialize a keyspace for each new test
https://github.com/jsevellec/cassandra-unit/wiki/Spring-for-Cassandra-unit
Related
It seems pass when a run the test individually but fine when fair when running the class .I have tried to use #Before and #After annotatoon.And only the Dirtiescontext most fit my case.My question is any alternative for dirtiescontext(too slow) or any method can suit my case?
My test code:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
#AutoConfigureMockMvc
class UserCoreControllerTest {
#Autowired
private UserRepository userRepository;
#Autowired
private UserCoreService userCoreService;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private MockMvc mockMvc;
/*
*//* #BeforeTestMethod
public void setUp(){
System.out.println("begin");
userRepository.save(mockUser());
}
#AfterTestMethod
public void tearOff(){
System.out.println("end");
userRepository.deleteById(1);
}
*/
private User mockUser(){
User user= new User(0,"admin","admin",null,null,"yl","sd"
,"434","dsf",null,4,2,new ArrayList<>());
user.getApplications().add(mockJobOrder("job1title","b"));
user.getApplications().add(mockJobOrder("job2title","d"));
return user;
}
private JobOrder mockJobOrder(String title,String des){
return new JobOrder(0,1,title,des,null,null,0,2,false,0,null);
}
#Test
void getProfile() throws Exception {
userRepository.save(mockUser());
mockMvc.perform(get("/UserJob/getProfile/{id}", 1)
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.username")
.value("admin"))
.andDo(print());
}
#Test
void getUserByName() throws Exception {
userRepository.save(mockUser());
mockMvc.perform(get("/UserJob/get/Byusername/{username}", "admin")
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.password").value("admin"));
//if cant found user
MvcResult whatever = mockMvc.perform(get("/UserJob/get/Byusername/{username}", "whatever")
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andReturn();
Assertions.assertEquals("",whatever.getResponse().getContentAsString());
}
#Test
void updateUser() throws Exception {
userRepository.save(mockUser());
User updated=new User(1,"alex","admin",null,null,"yl","sd"
,"434","dsf",null,4,2,null);
String request=objectMapper.writeValueAsString(updated);
MvcResult mvcResult=mockMvc.perform(MockMvcRequestBuilders.put("/UserJob/updateuser")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON_VALUE)
.content(request)
)
.andExpect(status().isOk())
.andDo(print())
.andReturn();
Assertions.assertEquals("Successful update",mvcResult.getResponse().getContentAsString());
mockMvc.perform(get("/UserJob/getProfile/{id}", 1)
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.username")
.value("alex"))
.andDo(print());
}
#Test
void showApplicationshistory() throws Exception {
userRepository.save(mockUser());
mockMvc.perform(get("/UserJob/application/history/{id}", 1)
.accept(MediaType.APPLICATION_JSON_VALUE)
)
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].title").value("job1title"))
.andExpect(jsonPath("$[1].title").value("job2title"))
.andDo(print());
}
#Test
void addUser() throws Exception {
User user=mockUser();
user.setUsername("tom");
String request=objectMapper.writeValueAsString(user);
mockMvc.perform(MockMvcRequestBuilders.post("/UserJob/add/user")
.contentType(MediaType.APPLICATION_JSON)
.content(request)
.accept(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username").value("tom"));
}
Application properties(i test it on a real database with drop-create)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=*
spring.datasource.password=*
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.defer-datasource-initialization=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.sql.init.mode=always
logging.level.root=INFO
I guess that the test are failing because of some user constraints - since you insert the user a multiple times here.
There are multiple solutions for that.
Mark your test as transactional. The transaction is automatically rollbacked for tests.
Clear the tables manually after/before each test. Either by using the repository.deleteAll() or truncate the whole db with some sql statements.
Just as a side information: You can also use testcontainers instead of having a persistente database locally. See https://www.testcontainers.org/ or even better: a wrapper library around it: https://github.com/PlaytikaOSS/testcontainers-spring-boot
I'm trying to read from DB2 with a stored procedure ( SP ) that returns records based on page size and start page among other input parameters. My goal is however to request all the records in one call and load another table in Mysql using Spring Batch. Seems straight forward enough, however when I run the job below it returns the correct number of records as per the SP but the records are skewed and duplicated as if the transactions are not working properly. The reader uses one #Primary Datasources ( DS ), for the DB2 read and another DS for the JpaItemWriter destination (LocalContainerEntityManagerFactoryBean).
My understanding is that DB2 and Mysql returns a ResultSet so don't know why I am forced to add these lines to avoid Invalid Parameter Type or Cursor position errors....
reader.setVerifyCursorPosition(false);
reader.setRefCursorPosition(0);
I am reading from DB2 then inserting into a MySql table based on the supplied AccountCashRowMapper. It's a strange situation that almost works, the listener doesn't report any errors.
Spring Batch is supposed to inherently manage the transactions for me, however what configuration or code is missing to make this work ?
#EnableBatchProcessing
public class LoadAccountCashTableJob {
private static final Logger logger = LoggerFactory.getLogger(LoadAccountCashTableJob.class);
#Autowired
ApplicationContext context;
#Autowired
public AccountCashRepository repo;
#Autowired
public EntityManager em;
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
private AccountCashProcessor accountCashProcessor;
#Autowired
private TaskExecutor taskExecutor;
#Autowired
#Qualifier("originDataSource")
protected DataSource dataSource;
#Qualifier(value = "AccountCashJob")
#Bean
public Job AccountCashJob() throws Exception{
return this.jobBuilderFactory.get("AccountCashJob")
.start(this.accountCashStep())
.build();
}
#Bean
public Step accountCashStep() throws Exception{
return this.stepBuilderFactory.get("accountCashStep")
//.transactionManager(transactionManager)
.<String, String>chunk(500)
.reader(this.cashSPReader(dataSource))
.processor(accountCashProcessor)
.writer(this.accountCashItemWriter() )
.taskExecutor(taskExecutor)
.listener(new ItemFailureLoggerListener())
.build();
}
#Bean(destroyMethod="")
#StepScope
public StoredProcedureItemReader cashSPReader (DataSource dataSource) throws Exception {
StoredProcedureItemReader reader = new StoredProcedureItemReader();
SqlParameter[] parameters = new SqlParameter[7];
parameters[0] = new SqlParameter("DIV", Types.VARCHAR);
parameters[1] = new SqlParameter("PageDebut", Types.SMALLINT);
parameters[2] = new SqlParameter("NbParPage",Types.SMALLINT);
parameters[3] = new SqlInOutParameter("O_NB_PAGES",Types.SMALLINT);
parameters[4] = new SqlInOutParameter("O_RC",Types.INTEGER);
parameters[5] = new SqlInOutParameter("O_RC_DSC",Types.VARCHAR);
parameters[6] = new SqlReturnResultSet(ACCOUNTCASH_RESULT_SET, new AccountCashRowMapper());
reader.setDataSource(dataSource);
reader.setProcedureName("VMDTSTSP.db2cdb_List_Positions_Encaisse");
reader.setRowMapper(new AccountCashRowMapper()); //FOR the output records..
reader.setParameters(parameters);
reader.setPreparedStatementSetter(new SPParamSetter());
reader.afterPropertiesSet();
logger.info("reader.getSql()>>>>>>>>>>>>>>"+reader.getSql());
reader.setVerifyCursorPosition(false);
reader.setRefCursorPosition(0);
return reader;
}
#Bean(destroyMethod="")
#StepScope
public JpaItemWriter<AccountCash> accountCashItemWriter() {
EntityManagerFactory emf = (EntityManagerFactory) context.getBean("entityManagerFactory");
em.createNativeQuery("TRUNCATE TABLE purefacts.account_cash").executeUpdate();
JpaItemWriter<AccountCash> accountCashJpaItemWriter = new JpaItemWriter<>();
accountCashJpaItemWriter.setEntityManagerFactory(emf);
accountCashJpaItemWriter.setUsePersist(true);
return accountCashJpaItemWriter;
}
public static class SPParamSetter implements PreparedStatementSetter {
#Override
public void setValues(PreparedStatement ps) throws SQLException {
AS400JDBCCallableStatement eventCallableSt=(AS400JDBCCallableStatement)ps;
eventCallableSt.setString(1, ACCOUNTCASH_DIV);
eventCallableSt.setInt(2, ACCOUNTCASH_START_PAGE);
eventCallableSt.setInt(3,ACCOUNTCASH_PAGE_SIZE);
eventCallableSt.registerOutParameter(4, Types.SMALLINT);
eventCallableSt.setInt(4, ACCOUNTCASH_O_NB_PAGES);
eventCallableSt.registerOutParameter(5, Types.INTEGER);
eventCallableSt.setInt(5, ACCOUNTCASH_O_ERROR_CODE);
eventCallableSt.registerOutParameter(6, Types.VARCHAR);
eventCallableSt.setString(6, ACCOUNTCASH_O_ERROR_DESCRIPTION);
eventCallableSt.getResultSet();
}
}
public class ItemFailureLoggerListener extends ItemListenerSupport {
private Log logger = LogFactory.getLog("item.error");
public void onReadError(Exception e) {
logger.error("Encountered error on read", e);
}
public void onWriteError(Exception ex, List items) {
logger.error("Encountered error on write", ex);
}
}
}
I'm trying to write a unit test for an MDB. The goal of my test is to make sure that the logic in the MDB can identify the correct type of object in the ObjectMessage and process it. However, I can't figure out how to make an ObjectMessage so I can test it. I keep getting null pointer exceptions.
Here is my unit test:
/**
* Test of the logic in the MDB
*/
#RunWith(JMockit.class)
#ExtendWith(TimingExtension.class)
class MDBTest
{
protected MyMDB mdb;
#BeforeEach
public void setup() throws NamingException, CreateHeaderException, DatatypeConfigurationException, PropertiesDataException
{
mdb = new MyMDB();
}
/**
* Test the processing of the messages by the MDB
*/
#Test
void testReceivingMessage() throws JMSException, IOException
{
MyFirstObject testMsg = getTestMessage();
ObjectMessage msg = null;
Session session = null;
new MockUp<ObjectMessage>()
{
#Mock
public void $init()
{
}
#Mock
public Serializable getObject()
{
return testMsg;
}
};
new MockUp<Session>()
{
#Mock
public void $init()
{
}
#Mock
public ObjectMessage createObjectMessage(Serializable object)
{
return msg;
}
};
// !!!! Null pointer here on Session !!!!
ObjectMessage msgToSend = session.createObjectMessage(testMsg);
mdb.onMessage(msgToSend);
assertEquals(1, mdb.getNumMyFirstObjectMsgs());
}
/**
* Create a Test Message
*
* #return the test message
* #throws IOException
*/
protected MyFirstObject getTestMessage) throws IOException
{
MyFirstObject myObj = new MyFirstObject();
myObj.id = 0123;
myObj.description = "TestMessage";
return myObj;
}
}
I feel like I should be able to initialize Session somehow, but I need to do it without using an additional library like Mockrunner.
Any suggestions?
I would try to address this in a different style. Provide a mock client, that will just mock the right API.
We should mock only a set of functions required for message retrieval and processing but that means we might have to provide a custom implementation for some of the APIs available in the EJB/JMS library. The mock client will have a function to push messages on a given topic/queue/channel, message can be simple String.
A simple implementation might look like this, in this other methods have been omitted for simplicity.
// JMSClientImpl is an implementation of Connection interface.
public class MyJmsTestClient extends JMSClientImpl{
Map<String, String> channelToMessage = new ConcurrentHashMap<>();
public Map<String, String> getMessageMap(){
return channelToMessage;
}
public void enqueMessage(String channel, String message){
channelToMessage.put(channe, message);
}
#Override
public Session createSession(){
return new MyTestSession(this);
}
}
// A class that implements some of the methods from session interface
public MyTestSession extends SessionImpl{
private MyJmsTestClient jmsClient;
MyTestSession(MyJmsTestClient jmsClient){
this.jmsClient = jmsClient;
}
// override methods that fetches messages from remote JMS
// Here you can just return messages from MyJmsTestClient
// override other necessary methods like ack/nack etc
MessageConsumer createConsumer(Destination destination) throws JMSException{
// returns a test consume
}
}
A class that implements methods from MessageConsumer interface
class TestMessageConsumer extends MessageConsumerImpl {
private MyJmsTestClient jmsClient;
private Destination destination;
TestMessageConsumer(MyJmsTestClient jmsClient, Destination destination){
this.jmsClient = jmsClient;
this.destination = destination;
}
Message receive() throws JMSException{
//return message from client
}
}
There's no straight forward, you can see if there're any library that can provide you embedded JMS client feature.
This is a follow up question to Spring Integration Executor Channel using annotations code sample.
System diagram is attached .
I am trying to test the box highlighted in red by posting a message into 'Common channel' and reading from REPLY_CHANNEL set in the msg.
'Common channel' is a publish subscribe channel.
REPLY_CHANNEL is a QueueChannel.
Since this is a JUnit test, I have mocked jdbcTemplate, datasource and the Impl to ignore any DB calls.
My issue is:
When I post a message onto 'Common Channel', I do not receive any message on the REPLY_CHANNEL. The junit keeps waiting for a response.
What should I change to get a response on the REPLY_CHANNEL?
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(loader = AnnotationConfigContextLoader.class) --------- 1
#ActiveProfiles("test")
public class QueuetoQueueTest {
#Configuration
static class ContextConfiguration { ------------------------------------- 2
#Bean(name = "jdbcTemplate")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplateMock = Mockito.mock(JdbcTemplate.class);
return jdbcTemplateMock;
}
#Bean(name = "dataSource")
public DataSource dataSource() {
DataSource dataSourceMock = Mockito.mock(DataSource.class);
return dataSourceMock;
}
#Bean(name = "entityManager")
public EntityManager entityManager() {
EntityManager entityManagerMock = Mockito.mock(EntityManager.class);
return entityManagerMock;
}
#Bean(name = "ResponseChannel")
public QueueChannel getReplyQueueChannel() {
return new QueueChannel();
}
//This channel serves as the 'common channel' in the diagram
#Bean(name = "processRequestSubscribableChannel")
public MessageChannel getPublishSubscribeChannel() {
return new PublishSubscribeChannel();
}
}
#Mock
DBStoreDaoImpl dbStoreDaoImpl;
#Test
public void testDBConnectivity() {
Assert.assertTrue(true);
}
#InjectMocks -------------------------------------------------------------- 3
StoretoDBConfig storetoDBConfig = new StoretoDBConfig();
#Autowired
#Qualifier("ResponseChannel")
QueueChannel ResponseChannel;
#Autowired
#Qualifier("processRequestSubscribableChannel")
MessageChannel processRequestSubscribableChannel;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void outboundtoQueueTest() {
try {
when(dbStoreDaoImpl.storeToDB(any()))
.thenReturn(1); ----------------------------------------------- 4
//create message
Message message = (Message<String>) MessageBuilder
.withPayload("Hello")
.setHeader(MessageHeaders.REPLY_CHANNEL, ResponseChannel)
.build();
//send message
processRequestSubscribableChannel.send(message);
System.out
.println("Listening on InstructionResponseHandlertoEventProcessorQueue");
//wait for response on reply channel
Message<?> response = ResponseChannel.receive(); ----------------------- 5
System.out.println("***************RECEIVED: "
+ response.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Load 'ContextConfiguration' for JUnit so that DB is not accessed.
This is how you load custom configuration in JUnit as per https://spring.io/blog/2011/06/21/spring-3-1-m2-testing-with-configuration-classes-and-profiles
Inside the config class, we mock jdbcTemplate, dataSource, entityManager and define the 'common channel' on which the request is posted and ResponseChannel.
Inject jdbcTemplate, dataSource mock into StoretoDBConfig so that the DB is not hit
Mock DaoImpl class so that DB calls are ignored
The test blocks here because there is no response on the REPLY_CHANNEL
UPDATED CODE:
Code inside 5 (the class that reads from common channel):
#Configuration
class HandleRequestConfig {
//Common channel - refer diagram
#Autowired
PublishSubscribeChannel processRequestSubscribableChannel;
//Step 9 - This channel is used to send queue to the downstream system
#Autowired
PublishSubscribeChannel forwardToExternalSystemQueue;
public void handle() {
IntegrationFlows.from("processRequestSubscribableChannel") // Read from 'Common channel'
.wireTap(flow->flow.handle(msg -> System.out.println("Msg received on processRequestSubscribableChannel"+ msg.getPayload())))
.handle(RequestProcessor,"validateMessage") // Perform custom business logic - no logic for now, return the msg as is
.wireTap(flow->flow.handle(msg -> System.out.println("Msg received on RequestProcessor"+ msg.getPayload())))
.channel("forwardToExternalSystemQueue"); // Post to 'Channel to another system'
}
}
//Code inside step 8 - 'Custom Business Logic'
#Configuration
class RequestProcessor {
public Message<?> validateMessage(Message<?> msg) {
return msg;
}
}
WHAT I AM TRYING TO ACHIEVE:
I have individual junit test cases for the business logic. I am trying to test that when the request is posted into the 'common channel', the response is received on 'channel to another system'.
Why I cannot use the original ApplicationContext: Because it connects to the DB, and I do not want my JUnit to connect to the DB or use an embedded database. I want any calls to the DB to be ignored.
I have set the reply channel to 'ResponseChannel', shouldn't the 'Custom Business Logic' send its response to 'ResponseChannel'?
If I have to listen on a different channel for the response, I am willing to do so. All I want to test is whether the message I am sending on 'common channel' is received on 'channel to other system'.
UPDATE 2:
Addressing Artem's questions.
Thankyou Artem for your suggestions.
Is 'HandlerRequestConfig' included in the test configuration? - We cannot directly call the handle() method. Instead I thought if I post on 'processRequestSubscribableChannel', the handle() method inside HandleRequestConfig will be invoked since it listens on the same channel. Is this wrong? How do I test HandleRequestConfig.handle() method then?
I added wiretap to the end of each step in HandleRequestConfig (code updated). I find that none of the wiretap message is printed. This means that the msg I am posting is not even reaching the input channel 'processRequestSubscribableChannel'. What am I doing wrong?
NOTE: I tried removing the 'processRequestSubscribableChannel' bean inside Configuration (so that the actual 'processRequestSubscribableChannel' in the applicationContext is used). I am getting an unsatisfied dependency error - Expected atleast 1 bean with configuration PublishSubscribeChannel.
Update 3: Posted details Artem requested.
#RunWith(SpringRunner.class)
#SpringBootTest
public class QueuetoQueueTest {
// Step 1 - Mocking jdbcTemplate, dataSource, entityManager so that it doesn't connect to the DB
#MockBean
#Qualifier("jdbcTemplate")
JdbcTemplate jdbcTemplate;
#MockBean
#Qualifier("dataSource")
public DataSource dataSource;
#MockBean
#Qualifier("entityManager")
public EntityManager entityManager;
#Bean(name = "ResponseChannel")
public PublishSubscribeChannel getReplyQueueChannel() {
return new PublishSubscribeChannel();
}
//Mocking the DB class
#MockBean
#Qualifier("dbStoreDaoImpl")
DBStoreDaoImpl dbStoreDaoImpl ;
//Inject the mock objects created above into the flow that stores data into the DB.
#InjectMocks
StoretoDBConfig storetoDBConfig = new StoretoDBConfig();
//Step 2 - Injecting MessageChannel used in the actual ApplicationContext
#Autowired
#Qualifier("processRequestSubscribableChannel")
MessageChannel processRequestSubscribableChannel;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void outboundtoQueueTest() {
try {
when(dbStoreDaoImpl.storeToDB(any()))
.thenReturn(1);
//create message
Message message = (Message<?>) MessageBuilder
.withPayload("Hello")
.build();
//send message - this channel is the actual channel used in ApplicationContext
processRequestSubscribableChannel.send(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
ERROR I AM GETTING: The code tries to connect to the DB and throws an error.
UPDATE 1: Code inside StoretoDBConfig
#Configuration
#EnableIntegration
public class StoretoDBConfig {
#Autowired
DataSource dataSource;
/*
* Below code is irrelevant to our current problem - Including for reference.
*
* storing into DB is delegated to a separate thread.
*
* #Bean
* public TaskExecutor taskExecutor() {
* return new SimpleAsyncTaskExecutor();
* }
*
* #Bean(name="executorChannelToDB")
* public ExecutorChannel outboundRequests() {
* return new ExecutorChannel(taskExecutor());
* }
* #Bean(name = "DBFailureChannel")
* public static MessageChannel getFailureChannel() {
* return new DirectChannel();
* }
* private static final Logger logger = Logger
* .getLogger(InstructionResponseHandlerOutboundtoDBConfig.class);
*/
#Bean
public IntegrationFlow handle() {
/*
* Read from 'common channel' - processRequestSubscribableChannel and send to separate thread that stores into DB.
*
/
return IntegrationFlows
.from("processRequestSubscribableChannel")
.channel("executorChannelToDB").get();
}
}
CODE THAT STORES INTO DB ON THE SEPARATE THREAD:
#Repository
public class DBStoreDaoImpl implements DBStoreDao {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setJdbcTemplate(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
#Transactional(rollbackFor = Exception.class)
#ServiceActivator(inputChannel = "executorChannelToDB")
public void storetoDB(Message<?> msg) throws Exception {
String insertQuery ="Insert into DBTable(MESSAGE) VALUES(?)";
jdbcTemplate.update(insertQuery, msg.toString());
}
}
Please, show us what is subscribed to that Common channel. Your diagram somehow is not related to what you show us. The code you demonstrate is not full.
The real problem with the replyChannel that something really has to send a message to it. If your flow is just one-way - send, store and nothing to return, - then you indeed won't get anything for this one. That's why would to show those channel adapters.
The best way to observe the message journey is to turn on debug logging for the org.springframework.integration category.
Although I see that you declare those channels as is in the ContextConfiguration and there is really no any subscribers to the getRequestChannel. Therefore nobody is going to consume your message and, of course, nobody is going to send you a reply.
Please, reconsider your test class to use the real application context. Otherwise it is fully unclear what you would like to achieve if you really don't test your flow...
I'm pretty new to programming with java but I've tried to directly start with unit-testing and therefore also used JMock. I have already implemented some test-cases (with JMock) that work, but this one I just can't get to run.
What I did:
I wrote a test-class which creates a mock object and then I'm expectation one (using oneOf) invocation. After running the unit test it says it fails (but the logs say otherwise, as i print out the data I returned at the invocation using will(returnValue(x)).
The next funny/weird thing is - if I change the oneOf to "never" the unit test succeeds, but it throws an Exception:
Exception in thread "Thread-2" java.lang.AssertionError: unexpected invocation: blockingQueue.take()
expectations:
expected never, never invoked: blockingQueue.take(); returns
what happened before this: nothing!
Here the code:
#RunWith(JMock.class)
public class ExecuteGameRunnableTest {
private Mockery context = new JUnit4Mockery();
private Thread testObject;
private BlockingQueue<Game> queueMock;
private Executor executorMock;
#SuppressWarnings("unchecked")
#Before
public void setUp() {
queueMock = context.mock(BlockingQueue.class);
executorMock = context.mock(Executor.class);
testObject = new Thread(new ExecuteGameRunnable(queueMock, executorMock, true));
}
#After
public void tearDown() {
queueMock = null;
executorMock = null;
testObject = null;
}
#Test
public void testQueueTake() throws InterruptedException {
final Game game = new Game();
game.setId(1);
game.setProcessing(false);
context.checking(new Expectations() {{
never(queueMock).take(); will(returnValue(game));
}});
testObject.start();
context.assertIsSatisfied();
}
}
and the runnable that I'm testing:
public class ExecuteGameRunnable implements Runnable {
private BlockingQueue<Game> queue;
private Executor executor;
private Boolean unitTesting = false;
static Logger logger = Logger.getLogger(ExecuteGameRunnable.class);
public ExecuteGameRunnable(BlockingQueue<Game> queue, Executor executor) {
this.queue = queue;
this.executor = executor;
}
public ExecuteGameRunnable (BlockingQueue<Game> queue, Executor executor, Boolean unitTesting) {
this(queue,executor);
this.unitTesting = unitTesting;
}
public void run() {
try {
do {
if (Thread.interrupted()) throw new InterruptedException();
Game game = queue.take();
logger.info("Game "+game.getId()+" taken. Checking if it is processing"); // THIS ONE PRINTS OUT THE GAME ID THAT I RETURN WITH JMOCK-FRAMEWORK
if (game.isProcessing()) {
continue;
}
game.updateProcessing(true);
executor.execute(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
}
});
} while (!unitTesting);
} catch (InterruptedException ex) {
logger.info("Game-Execution-Executor interrupted.");
return;
} catch (DataSourceException ex) {
logger.fatal("Unable to connect to DB whilst executing game: "+id_game,ex);
return;
}
}
}
JMock isn't thread safe. It's intended to support unit testing, rather than what is a very small integration test. Frankly, in this case I'd use a real BlockingQueue rather than a mock one. And there is no way you should have a unitTesting flag in your production code.
One more thing, you don't need to set the fields in the test class to null, jUnit flushes the instance for every test.