Running tests in Spring Boot - java

I have this test class
I've generated a Spring Boot web application using Spring Initializr, using embedded Tomcat + Thymeleaf template engine, and package as an executable JAR file.
Technologies used :
Spring Boot 1.4.2.RELEASE, Spring 4.3.4.RELEASE, Thymeleaf 2.1.5.RELEASE, Tomcat Embed 8.5.6, Maven 3, Java 8
my classes:
#RunWith(SpringRunner.class)
#SpringBootTest
public class TdkApplicationTests {
#Test
public void contextLoads() {
}
}
#Configuration
public class PersistenceConfig {
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
#Bean
public IOTEcoSystemManager iOTEcoSystemManager() {
return new IOTEcoSystemManagerImpl();
}
#Bean
public DeviceEventRepository deviceEventRepository() {
return new JdbcDeviceEventRepository();
}
#Bean
public DeviceRepository deviceRepository() {
return new JdbcDeviceRepository();
}
/**
* Creates an in-memory "books" database populated
* with test data for fast testing
*/
#Bean
public DataSource dataSource(){
return
(new EmbeddedDatabaseBuilder())
.addScript("classpath:db/H2.schema.sql")
.addScript("classpath:db/H2.data.sql")
.build();
}
}
#ContextConfiguration(classes={PersistenceConfig.class})
#RunWith(SpringRunner.class)
public class BookManagerTests {
/**
* The object being tested.
*/
#Autowired
BookManager bookManager;
#Test
public void testfindDeviceByKey() {
Device device = bookManager.findDeviceByKey("C380F");
assertNotNull(device);
assertEquals("C380F", device.getDeviceKey());
assertEquals(1, device.getId().longValue());
}
#Test
public void testfindDeviceByKeyFail() {
Device device = null;
try {
device = bookManager.findDeviceByKey("C380FX");
} catch (EmptyResultDataAccessException erdae) {
assertNotNull(erdae);
}
assertNull(device);
}
}
#Service("bookManager")
public class BookManagerImpl implements BookManager {
...
}
If I run all the test of the package I got this error:
unique constraint or index violation; SYS_PK_10092 table: T_COMPANY
because the script runs twice. If I remove
classes={PersistenceConfig.class}
from BookManagerTests I got this dependency problem
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
If I run both test individually everything is OK

PersistenceConfig and its content seem to be created twice: first time as ordinary bean and second time as config (that's right). I recommend to create single main test class with all annotations and then extend your tests (without any annotation) from it.
#ContextConfiguration(classes={PersistenceConfig.class})
#RunWith(SpringRunner.class)
#SpringBootTest
public abstract class MainTest {
}
public BookManagerTest extends MainTest {
...
}

To me it seems the constraint validation happens because PersistentConfig is loaded twice, and that masks your second problem. Where is BookManager defined? Are you sure your scanning the classpath correctly for it, if it's annotated with #repository?

Related

spring boot - integration test autowired interface no such bean found

I have a spring-boot app that now needs to support multiple Object stores and selectively use the desired store based on the environment. Essentially what i have done is create an interface that each store repository then implements.
I have simplified the code for the examples.
I have created 2 beans for each store type based on the spring profile determining the env:
#Profile("env1")
#Bean
public store1Sdk buildClientStore1() {
return new store1sdk();
}
#Profile("env2")
#Bean
public store2Sdk buildClientStore2() {
return new store2sdk();
}
in the service layer I have autowired the interface and then in the repositories i have used #Profile to specify which instance of the interface to use.
public interface ObjectStore {
String download(String fileObjectKey);
...
}
#Service
public class ObjectHandlerService {
#Autowired
private ObjectStore objectStore;
public String getObject(String fileObjectKey) {
return objectStore.download(fileObjectKey);
}
...
}
#Repository
#Profile("env1")
public class Store1Repository implements ObjectStore {
#Autowired
private Store1Sdk store1client;
public String download(String fileObjectKey) {
return store1client.getObject(storeName, fileObjectKey);
}
}
When I start the application with the configured "env" this actually runs as expected. however when running the test I get the "no qualifying bean of type ObjectStore. expected at least 1 bean which qualifies as autowire candidate."
#ExtendWith({ SpringExtension.class })
#SpringBootTest(classes = Application.class)
#ActiveProfiles("env1,test")
public class ComposerServiceTest {
#Autowired
private ObjectHandlerService service;
#Test
void download_success() {
String response = service.getObject("testKey");
...
}
}
As noted in the #ActiveProfile on the test class there are some other environments e.g. dev,test,prod. I have tried playing around with Component scan, having impl and interface in the same package, etc, to no success. I feel like I am missing something obvious with the test setup. But could be something with my overall application config? my main aim with the solution is to avoid having something a long the lines of
if (store1Sdk != null) {
store1Sdk.download(fileObjectKey);
}
if (store2Sdk != null) {
store2Sdk.download(fileObjectKey);
}
Try #ActiveProfiles({"env1", "test"}).
Activate multiple profiles using #ActiveProfiles and specify profiles as an array.
this probrom because Store1Repository use #Profile("env1"), when you use #test,this class not invoke. try delete #Profile("env1") of Store1Repository.
if you use #test, both of store1Sdk/store2Sdk don't instanse, try add default instanse.eg:
#Bean
public store2Sdk buildClientStoreDefault() {
return new store2sdk();
}

How can I programmatically autowire field which is not part of bean?

In a Spring Boot application I am working on, I have a class which is not annotated as bean (#Component), but contains an autowired field:
public class One{
#Autowired
private Two x;
public getX(){
return x;
}
}
In the configuration xml of the Spring application the class One is marked as bean which makes that the variable x gets initialized when I run the application.
Now I have written a test that doesn't seem to use the spring xml configuration. So I tried to do it manually:
#RunWith(SpringRunner.class)
public class Test{
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX()); //null
}
}
How can I make Spring inject the correct code so that x is not null in my test?
Just tell the test what config to use:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:applicationContext_test.xml" })
public class Test{
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX()); //null
}
}
See here for doc
Alernative to #Essex Boy approach:
use a custom configuration in your test:
#RunWith(SpringRunner.class)
public class TestClass {
#Configuration
#ComponentScan
static class ConfigurationClass {
#Bean
public One makeOne() {
return new One();
}
}
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX());
}
}
Essex Boy's approach runs an "integration test" because it starts up Spring for the test.
Usually for Unit Tests, you want to mock your depencies; those can be "autowired".
#RunWith(MockitoJUnitRunner.class) // necessary for the annotations to work
public class YourTest {
// this is a mock
#Mock
private Two mockedTwo;
#InjectMocks
// this is automatically created and injected with dependencies
private One sut;
#Test
public void test() {
assertNotNull(sut.getX());
sut.doStuff();
verify(mockedTwo).wasCalled();
}
}

Making a bean primary without annotation or xml config

My project has a dependency on another one, and imports beans from it (using #ImportResource("foo.xml")).
foo.xml defines two datasources (datasource1 and datasource2), I would like to make datasource1 a primary (so all auto-configurations of Spring Boot will work).
Is it possible? I found out that there is a DefaultListableBeanFactory that has determinePrimaryCandidate method.
So the idea is to create my own ListableBeanFactory, that would extend the DefaultListableBeanFactory, but how to force Spring Boot to use my implementation?
Or maybe there is another, easier way to mark a given bean as primary (without changing the configuration where it is defined).
You can create a configuration in your project, which builds a new data source annotated as #Primary bean. This new data source will be the datasource1, which will be injected by spring to the new data source factory method. Here you have the working example.
The config:
#SpringBootApplication
public class BeanSpringExampleApplication
{
#Bean(name = "dataSource1")
public FakeDataSource dataSource1()
{
return new FakeDataSource("dataSource1");
}
#Bean(name = "dataSource2")
public FakeDataSource dataSource2()
{
return new FakeDataSource("dataSource2");
}
#Bean
#Primary
public FakeDataSource primaryDataSource(
#Qualifier("dataSource1") FakeDataSource dataSource1)
{
return dataSource1;
}
}
Here you see three beans (using FakeDataSource class), which simulate your situation. The primaryDataSource bean factory method simply returns the dataSource1 (it's just a mere data source selector).
The FakeDataSource is just a placeholder, to make example runnable:
public class FakeDataSource
{
private final String fakeProperty;
public FakeDataSource(String id)
{
fakeProperty = id;
}
/**
* #return the fakeProperty
*/
public String getFakeProperty()
{
return fakeProperty;
}
}
Finally, a test which proves everything is working:
#RunWith(SpringRunner.class)
#SpringBootTest
public class BeanSpringExampleApplicationTests
{
#Autowired
private FakeDataSource fakeDataSource;
#Test
public void should_AutowirePrimaryDataSource() throws Exception
{
assertEquals("dataSource1", fakeDataSource.getFakeProperty());
}
}

Spring dependency injection into Spring TestExecutionListeners not working

How can I use Spring dependency injection into a TestExecutionListener class I wrote extending AbstractTestExecutionListener?
Spring DI does not seem to work with TestExecutionListener classes.
Example of issue:
The AbstractTestExecutionListener:
class SimpleClassTestListener extends AbstractTestExecutionListener {
#Autowired
protected String simplefield; // does not work simplefield = null
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
System.out.println("simplefield " + simplefield);
}
}
Configuration file:
#Configuration
#ComponentScan(basePackages = { "com.example*" })
class SimpleConfig {
#Bean
public String simpleField() {
return "simpleField";
}
}
The JUnit Test file:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SimpleConfig.class })
#TestExecutionListeners(mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS, listeners = {
SimpleClassTestListener.class })
public class SimpleTest {
#Test
public void test(){
assertTrue();
}
}
As highlighted in the code comment, when I run this, it will print "simplefield null" because simplefield never gets injected with a value.
Just add autowiring for the whole TestExecutionListener.
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
testContext.getApplicationContext()
.getAutowireCapableBeanFactory()
.autowireBean(this);
// your code that uses autowired fields
}
Check sample project in github.
In the case of Spring Boot 2 using
estContext.getApplicationContext()
.getAutowireCapableBeanFactory()
.autowireBean(this)
was triggering the creation of the Spring context before the #SpringBootTest base class was created. This missed then some critical configuration parameters in my case. I had to use testContext.getApplicationContext().getBean( in beforeTestClass for getting a bean instance.

#ComponentScan not working in test with spring-boot-starter-test

I am attempting to test my #Service and #Repository classes in my project with spring-boot-starter-test and #Autowired is not working for the classes I'm testing.
Unit test:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = HelloWorldConfiguration.class
//#SpringApplicationConfiguration(classes = HelloWorldRs.class)
//#ComponentScan(basePackages = {"com.me.sbworkshop", "com.me.sbworkshop.service"})
//#ConfigurationProperties("helloworld")
//#EnableAutoConfiguration
//#ActiveProfiles("test")
// THIS CLASS IS IN src/test/java/ AND BUILDS INTO target/test-classes
public class HelloWorldTest {
#Autowired
HelloWorldMessageService helloWorldMessageService;
public static final String EXPECTED = "je pense donc je suis-TESTING123";
#Test
public void testGetMessage() {
String result = helloWorldMessageService.getMessage();
Assert.assertEquals(EXPECTED, result);
}
}
Service:
#Service
#ConfigurationProperties("helloworld")
// THIS CLASS IS IN /src/main/java AND BUILDS INTO target/classes
public class HelloWorldMessageService {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message=message;
}
}
The commented class annotations on the unit test represent the various things I've tried to get this working. The test and the project packages are in the same package paths and the #ComponentScan works fine from my entry point (#RestController class with main method). The service #ComponentScan's and #Autowire's fine in my #RestController class in the src/main/java side, but does not in the test. I am required to add it again as a #Bean in my #Configuration class in order for #Autowired to work. The class is otherwise in scope just fine and I can reference and instantiate it just fine from the test. The problem appears to be that #ComponentScan does not appear to correctly traverse multiple entries in my test runner classpath, in this case /target/test-classes and /target/classes.
The IDE I am using is IntelliJ IDEA 13.
UPDATE - here are HelloWorldRs and its config:
#RestController
#EnableAutoConfiguration
#ComponentScan
public class HelloWorldRs {
// SPRING BOOT ENTRY POINT - main() method
public static void main(String[] args) {
SpringApplication.run(HelloWorldRs.class);
}
#Autowired
HelloWorldMessageService helloWorldMessageService;
#RequestMapping("/helloWorld")
public String helloWorld() {
return helloWorldMessageService.getMessage();
}
}
...
#Configuration
public class HelloWorldConfiguration {
#Bean
public Map<String, String> map() {
return new HashMap<>();
}
// This bean was manually added as a workaround to the #ComponentScan problem
#Bean
public HelloWorldMessageService helloWorldMessageService() {
return new HelloWorldMessageService();
}
// This bean was manually added as a workaround to the #ComponentScan problem
#Bean
public HelloWorldRs helloWorldRs() {
return new HelloWorldRs();
}
}
First, I'd recommend to use a newer #RunWith(SpringRunner.class) but that makes no difference, it is just shorter (and recommended).
Second, from the #EnableAutoConfiguration I see that you are using spring boot - which is certainly a good thing. There are some good reasons why not to use #ComponentScan directly. Can you try the following?
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes=YourApplication_or_other_Configuration.class)
public class HelloWorldTest {
... etc.
I don't know if this will turn out to be the solution, but don't use the default package (i.e. don't put *.java in "src/main/java" directly), and definitely don't use a #ComponentScan or #EnableAutoConfiguration in the default package. You will end up killing your application on startup as it tries to scan everything on the classpath (including all the Spring libraries).
SpringBoot 2.7.3, JUnit 5.8.2
If you want to have full control about the spring's configuration (and not rely on the hidden magic of auto configuration) I suggest to create an explicit configuration class:
#ComponentScan(basePackages = { "my.package.to.scan" })
public class MySpringTestConfig
{
// just for spring configuration annotations
}
and reference it in your test class:
#ContextConfiguration(classes = { MySpringTestConfig.class })
#ExtendWith({ SpringExtension.class })
class MySpringTest
{
...
}

Categories