Junit for Spring Application and skip original configuration file - spring

My Spring class Class
public class DemoDAO{
public Map<String, Object> getQuery(String quer) {
ApplicationContext context = new ClassPathXmlApplicationContext("myConfig.xml");
SessionFactory sessionFactoryFacility = (SessionFactory) context.getBean("myBean");
Session session = sessionFactoryFacility.openSession();
Transaction tx = session.beginTransaction();
//--------------------
}
}
Now when I was trying to write Junit for the above class,
#ContextConfiguration
#ExtendWith(org.mockito.junit.jupiter.MockitoExtension.class)
#TransactionConfiguration(defaultRollback = true)
public class DemoTest {
#InjectMocks
DemoDAO demo;
#Test
public void getQueryTest() {
Map<String, Object> response = demo.getQuery("query");
}
}
Now when i try to debug this application, giving errors saying unknown host and other errors , Because it is always connecting to "myConfig.xml" and trying to fetch the details, but in my "myConfig.xml" all are configured PRODUCTION DB details. I don't want to connect Production DB while testing my DAO class. Please suggest how to test this application

Related

Passing an external property to JUnit's extension class

My Spring Boot project uses JUnit 5. I'd like to setup an integration test which requires a local SMTP server to be started, so I implemented a custom extension:
public class SmtpServerExtension implements BeforeAllCallback, AfterAllCallback {
private GreenMail smtpServer;
private final int port;
public SmtpServerExtension(int port) {
this.port = port;
}
#Override
public void beforeAll(ExtensionContext extensionContext) {
smtpServer = new GreenMail(new ServerSetup(port, null, "smtp")).withConfiguration(GreenMailConfiguration.aConfig().withDisabledAuthentication());
smtpServer.start();
}
#Override
public void afterAll(ExtensionContext extensionContext) {
smtpServer.stop();
}
}
Because I need to configure the server's port I register the extension in the test class like this:
#SpringBootTest
#AutoConfigureMockMvc
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
public class EmailControllerIT {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Value("${spring.mail.port}")
private int smtpPort;
#RegisterExtension
// How can I use the smtpPort annotated with #Value?
static SmtpServerExtension smtpServerExtension = new SmtpServerExtension(2525);
private static final String RESOURCE_PATH = "/mail";
#Test
public void whenValidInput_thenReturns200() throws Exception {
mockMvc.perform(post(RESOURCE_PATH)
.contentType(APPLICATION_JSON)
.content("some content")
).andExpect(status().isOk());
}
}
While this is basically working: How can I use the smtpPort annotated with #Value (which is read from the test profile)?
Update 1
Following your proposal I created a custom TestExecutionListener.
public class CustomTestExecutionListener implements TestExecutionListener {
#Value("${spring.mail.port}")
private int smtpPort;
private GreenMail smtpServer;
#Override
public void beforeTestClass(TestContext testContext) {
smtpServer = new GreenMail(new ServerSetup(smtpPort, null, "smtp")).withConfiguration(GreenMailConfiguration.aConfig().withDisabledAuthentication());
smtpServer.start();
};
#Override
public void afterTestClass(TestContext testContext) {
smtpServer.stop();
}
}
The listener is registered like this:
#TestExecutionListeners(value = CustomTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
When running the test the listener gets called but smtpPort is always 0, so it seems as if the #Value annotation is not picked up.
I don't think you should work with Extensions here, or in general, any "raw-level" JUnit stuff (like lifecycle methods), because you won't be able to access the application context from them, won't be able to execute any custom logic on beans and so forth.
Instead, take a look at Spring's test execution listeners abstraction
With this approach, GreenMail will become a bean managed by spring (probably in a special configuration that will be loaded only in tests) but since it becomes a bean it will be able to load the property values and use #Value annotation.
In the test execution listener you'll start the server before the test and stop after the test (or the whole test class if you need that - it has "hooks" for that).
One side note, make sure you mergeMode = MergeMode.MERGE_WITH_DEFAULTS as a parameter to #TestExecutionListeners annotation, otherwise some default behaviour (like autowiring in tests, dirty context if you have it, etc) won't work.
Update 1
Following Update 1 in the question. This won't work because the listener itself is not a spring bean, hence you can't autowire or use #Value annotation in the listener itself.
You can try to follow this SO thread that might be helpful, however originally I meant something different:
Make a GreenMail a bean by itself:
#Configuration
// since you're using #SpringBootTest annotation - it will load properties from src/test/reources/application.properties so you can put spring.mail.port=1234 there
public class MyTestMailConfig {
#Bean
public GreenMail greenMail(#Value(${"spring.mail.port"} int port) {
return new GreenMail(port, ...);
}
}
Now this configuration can be placed in src/test/java/<sub-package-of-main-app>/ so that in production it won't be loaded at all
Now the test execution listener could be used only for running starting / stopping the GreenMail server (as I understood you want to start it before the test and stop after the test, otherwise you don't need these listeners at all :) )
public class CustomTestExecutionListener implements TestExecutionListener {
#Override
public void beforeTestClass(TestContext testContext) {
GreenMail mailServer =
testContext.getApplicationContext().getBean(GreenMail.class);
mailServer.start();
}
#Override
public void afterTestClass(TestContext testContext) {
GreenMail mailServer =
testContext.getApplicationContext().getBean(GreenMail.class);
mailServer.stop();
}
}
Another option is autowiring the GreenMail bean and using #BeforeEach and #AfterEach methods of JUnit, but in this case you'll have to duplicate this logic in different Test classes that require this behavour. Listeners allow reusing the code.

Spring boot junit test #JpaDataTest not working due to missing dependency

I'm trying to write a test using my actual configuration.
In the normal spring context, I'm loading the dataSource using a #Autowired service that decrypts the database password from the properties.
It looks like this
#Configuration
public class DataBaseConfig {
#Value("${swat.datasource.url}")
private String dbURL;
#Value("${swat.datasource.driver-class-name}")
private String driverName;
#Value("${swat.datasource.username}")
private String userName;
#Value("${swat.datasource.password}")
private String hashedPassword;
#Autowired
EncryptionService encryptionService;
#Bean
public DataSource primaryDataSource() {
String password = encryptionService.decriptPassword(hashedPassword);
return DataSourceBuilder.create().url(dbURL).driverClassName(driverName).username(userName).password(password).build();
}
im now trying to run a test using #JpaDataTest (not the #SpringBootTest)
doing the following
#RunWith(SpringRunner.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace=Replace.NONE)
#Import(DataBaseConfig.class)
#TestPropertySource(value = "file:./executor.properties")
public class NewDeviceTest {
#Autowired
NewDeviceService newDeviceService;
#Test
public void loadNewDevices() {
List<NewDevice> devices = newDeviceService.findAll();
assertEquals(1, devices.size());
assertTrue(devices.get(0).isNew());
}
}
im getting a problem since the EncryptionService in the DataBaseConfig cannot be resolved
how do i tell the Test to first load this
i tried
#ComponentScan("com.wisemon.compliance.executor.service")
but it loads all the components and some of them have a behaviour that i dont want in my test (load initial db data... scheduling etc)

How to debug a Spring Boot application via SpringBootTest

I'm new to Spring Boot and I really like it especially when it comes to eliminate the boilerplate code.
I have created a test class to test my NBRController:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = NewBusinessRevitalizationApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource(properties = {"management.port=0"})
public class NBRControllerTest extends TestCase {
#LocalServerPort
private int port;
#Value("${local.management.port}")
private int mgt;
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void getApplicationByAgencyIdAndStatusTest() {
String uri = "http://localhost:" + this.port + "/nbr-services/applications/{status}?agencyIds=123456,56765,678576";
Map<String, String> vars = new HashMap<String, String>();
vars.put("status", "SAVED");
ResponseEntity<String> response = testRestTemplate.getForEntity(uri, String.class, vars);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
If I run it in debug mode I can only debug the Test class and not my NBRController class:
#RestController
#RequestMapping("/nbr-services")
public class NBRController {
#Autowired
private NBRServices nbrServices;
private static Logger logger = LoggerFactory.getLogger(NBRController.class);
#RequestMapping(value = "/configuration/environment/{environment}", method = RequestMethod.GET)
#ResponseBody
public String getConfiguration(#PathVariable("environment") String environment) throws RemoteException {
logger.debug("environment={}", environment);
String result = nbrServices.getConfiguration(environment);
return result;
}
}
I have tried to setup the Tomcat debug port but not luck.
The only way I can debug my NBRController is run it in debug mode and call my RestAPI from the browser, but I want to use my unit test. Thanks in advance!
I had this happening when I accidentally had 2 controller methods with the same path mapping.
Other alternatives for debugging:
Only mock the server using mockMVC
It is possible to debug a system by not using a split webEnvironment, but to use spring MockMVC to make direct method calls to controllers instead of http calls.
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.MOCK // this is the default
)
#AutoConfigureMockMvc
class MyTest {
#Autowired
private MockMvc mockMvc;
#Test public void myTest() {
mockMvc.perform("/mypath");
// ...
}
}
This will not actually make http calls between the jUnit class and the controller, so this http processing part would not be tested.
Start the server separately and attach a remote debugger
In the IDE, start the application can in debugging mode
When application it is up and running, start JUnit tests which contain any http client, e.g. RestAssured.
This will spawn 2 JVMs, but the IDE is connected to both, so all breakpoints work.
I am using Intellij 2020.3 and I could able to debug my controller.
Stop all the running instances from intellij.
2.Simply put debug pointers in right controller method and run your test case in debug mode.
Unless you are hitting wrong endpoint, it should work.
Also you can try to evaluate your testRestTemplate call in test case in debug mode, in case it is failing in network itself.
You're possibly running on a different port to the one you think you are.
The SpringBootTest annotation is what controls the test port e.g.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
In the Uri you are appending http://local host +port , which is not necessary , testRestTemplate does it for you. Remove that and try you may hit the debug point
Here is example to write Junit for Rest layer for Spring boot 2 + JUnit 5
#ExtendWith(MockitoExtension.class)
public class RestTest {
#InjectMocks
private Rest rest;
private MockMvc mockMvc;
#BeforeEach
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(rest).build();
}
#Test
public void getTest() throws Exception {
String url = "/test";
ResultActions resultActions = mockMvc.perform(get(url));
resultActions.andExpect(status().isOk());
}
}
#RestController
#RequestMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
public class Rest {
#GetMapping
public #ResponseBody String get() {
return "success";
}
}
Personally, i use maven-spring-boot plugin and when i debug, its from a Maven run config. Maybe that is what is wrong with what you are doing? The maven-spring-boot plugin, during test phase, will start spring boot server before the test runs.
If you want to do it with a command line app , then you have to manually load the Spring context and execute the main class from a couple lines of code, before your test runs. I don't remember off-hand how to do it.

GridFsTemplate NullPointerException in Service Unit Test Class (Tech Stack: Spring Data / Spring Boot / Micro Service / Mongodb )

I am developing a spring boot app.
The service method uploads a PDF into a mongodb repo using GridFsTemplate which is autowired in the service.
This file upload service method works as expected via postman rest client.
But, When I tried running a unit test; calling the same service method, the SpringData GridFsTemplate is not initialised (In MongoDB, you can use GridFS to store binary files). This results in the org.springframework.data.mongodb.gridfs.GridFsTemplate.store(...) throwing a NullPointerException.
Please, can you help, I have been stuck in this for a days.
Below is my service implementation:
#Service
public final class UploadServiceImpl implements UploadService {
#Autowired
private SequenceRepository sequenceDao;
#Autowired (required = true)
private GridFsTemplate gridFsTemplate;
#Override
public Long uploadFile(Invoice uploadedInvoice) {
ByteArrayInputStream byteArrayInputStream = null;
if (checkContentType(invoiceInfo.getContentType())) {
invoiceInfo.setPaymentID(sequenceDao.getNextSequenceId(INVOICE_UPLOAD_SEQ_KEY));
byteArrayInputStream = new ByteArrayInputStream(uploadedInvoice.getFileContent());
//Error thrown is java.lang.NullPointerException: null, where gridFsTemplate is null and basically autowire does not work when test is run.
GridFSFile gridFSUploadedFile= gridFsTemplate.store(byteArrayInputStream, invoiceInfo.getFileName(), invoiceInfo.getContentType(), invoiceInfo);
return 1l;
} else {
return 2l;
}
}
### Below is my Unit Test class for the service
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class UploadServiceTest {
#Mock
private SequenceRepository sequenceRepositoryMock;
#Autowired
private GridFsTemplate gridFsTemplateMock;
#Mock
private Invoice invoiceMock;
#InjectMocks
private static UploadService uploadService = new UploadServiceImpl();
DBObject fileMetaData = null;
DB db = null;
Jongo jongo = null;
#Before
public void setUp() throws Exception {
db = new Fongo("Test").getDB("Database");
jongo = new Jongo(db);
}
#Test
public void testUploadFile() {
//test 1
Long mockPaymentNo = new Long(1);
Mockito.when(sequenceRepositoryMock.getNextSequenceId(INVOICE_SEQUENCE)).thenReturn(mockPaymentNo);
assertEquals(mockPaymentNo, (Long) sequenceRepositoryMock.getNextSequenceId(INVOICE_SEQUENCE));
//test 2
Invoice dummyInvoice = getDummyInvoice();
InvoiceInfo dummyInvoiceInfo = dummyInvoice.getInvoiceInfo();
MongoCollection invoicesCollection = jongo.getCollection("invoices");
assertNotNull(invoicesCollection.save(dummyInvoiceInfo));
assertEquals(1, invoicesCollection.save(dummyInvoiceInfo).getN());
System.out.println("TEST 2 >>>>>>>>>>>>>>>>>> "+ uploadService);
//test 3 : The following line is the cause of the exception, the service method is called but the GridFsTemplate is not initialized when the test is run. But it works when the endpoint is invoked via postman
uploadService.uploadFile(dummyInvoice);
System.out.println("TEST 3 >>>>>>>>>>>>>>>>>> ");
}
}
The problem is because you use #InjectMocks to autowire your UploadService.
And UploadService autowire two other beans SequenceRepository and GridFsTemplate.
If you’re doing TDD or not (and we are able to change the test first)
– clients of this code don’t know about an additional dependency,
because it’s completely hidden
The Javadoc states:
Mockito will try to inject mocks only either by constructor injection,
setter injection, or property injection in order and as described
below. If any of the following strategy fail, then Mockito won’t
report failure; i.e. you will have to provide dependencies yourself.
The solution is to use UploadServiceImpl constructor to autowire beans:
#Service
public final class UploadServiceImpl implements UploadService {
private final SequenceRepository sequenceDao;
private final GridFsTemplate gridFsTemplate;
private final PlannerClient plannerClient;
#Autowired
public PlannerServiceImpl(PlannerClient plannerClient, GridFsTemplate gridFsTemplate, SequenceRepository sequenceDao) {
this.plannerClient = plannerClient;
}
...
}
When there are more dependencies needed, they’re clearly in sight because they are initialized in constructor
More detailed:
https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/

Why won't the transaction start in my junit test cases?

I have a Spring 3.1 MVC + Hibernate 3.6 project with its junit4 test suit. My problem is that there is no transaction starting in my test cases, even thought I added a #Transactional.
My test case calls a controller and a dao. In the controller, a transaction is started anyway, so it does not complain. In the dao, I added a #Transactional(propagation = Propagation.MANDATORY) to be sure it will take the test's transaction. And currently it raises an IllegalTransactionStateException, which I guess it means there is no current transaction.
I tried to create programmaticaly an transaction and it does work, which means the AOP proxy to get the dao service is not the cause of the problem. However I want to create a transaction with the #Transactional annotation.
here's my dao:
// ...imports...
#Repository("taskDao")
#Transactional(propagation = Propagation.MANDATORY)
public class TaskHome implements TaskDao {
private static final Log log = LogFactory.getLog(TaskHome.class);
private SessionFactory sessionFactory;
#Autowired
public TaskHome(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Task findById(int id) {
log.debug("getting Task instance with id: " + id);
try {
Task instance = (Task) this.sessionFactory.getCurrentSession().get(
Task.class, id); // exception raised here!
if (instance == null) {
log.debug("get successful, no instance found");
} else {
log.debug("get successful, instance found");
}
return instance;
} catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
...
}
Here's my test case:
// ...imports...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class TestTaskController {
private static ClassPathXmlApplicationContext context;
private static TaskDao taskDao;
#BeforeClass
public static void initHibernate() throws Exception {
context = new ClassPathXmlApplicationContext("applicationContext.xml");
taskDao = context.getBean("taskDao", TaskDao.class);
}
#Test
public void testOnSubmit() {
// expects an existing default transaction here
Task task = taskDao.findById(1); // fails already here
// ... calls the controller and does some tests.
}
}
I searched in all Spring's documentation and googled it in any way I could imagine, but I don't see why the transaction is not started.
Any help is very welcome.
When using #RunWith(SpringJUnit4ClassRunner.class) you should obtain beans from the application context created by SpringJUnit4ClassRunner rather than from your own one.
In your case things go wrong because #Transactional on the unit test creates a transaction in the application context managed by SpringJUnit4ClassRunner, but you call methods on the beans obtained from the application context created manually.
So, remove your #BeforeClass method and obtain TaskDao via autowiring:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class TestTaskController {
#Autowired
private TaskDao taskDao;
...
}
See also:
9.3.5 Spring TestContext Framework

Resources