Unit test with testNG in spring boot takes time to build project - java

I have created a web application in spring-boot. I am writing a unit tests with testNG for my business layer.
I have created Application class
#SpringBootApplication
public class TestApplication
{
public static void main(String[] args)
{
SpringApplication.run(TestApplication.class, args);
}
#Bean
Mapper mapper()
{
List<String> mappingFiles = new ArrayList<String>();
mappingFiles.add("dozer-mappings.xml");
return new DozerBeanMapper(mappingFiles);
}
}
My test classes are looks like
#ContextConfiguration(classes = { TestApplication.class })
public class CommissionRuleServiceTest extends AbstractTestNGSpringContextTests
{
#InjectMocks
#Autowired
MyService
#Mock
MyDAO;
#BeforeMethod
public void initMock()
{
MockitoAnnotations.initMocks(this);
}
#Test(dataProvider = "....")
......
......
}
When I run project it shows hugh log on console and it takes times say 20.00secs for just few small tests.
Some of statements from log are,
DEBUG o.s.c.i.s.PathMatchingResourcePatternResolver - Searching directory
DEBUG o.s.c.a.ConfigurationClassPostProcessor
DEBUG o.s.c.a.ClassPathBeanDefinitionScanner
DEBUG o.s.c.i.s.PathMatchingResourcePatternResolver
DEBUG o.s.b.f.s.DefaultListableBeanFactory
DEBUG o.a.c.b.converters.ArrayConverter
DEBUG org.dozer.loader.xml.XMLParser
DEBUG org.hibernate.cfg.SettingsFactory
DEBUG o.h.cfg.annotations.CollectionBinder
DEBUG o.h.cfg.annotations.TableBinder
DEBUG o.h.p.w.spi.MetamodelGraphWalker - Visiting attribute path : MyEntity
DEBUG o.s.b.f.s.DefaultListableBeanFactory
DEBUG org.hibernate.SQL
Why it is taking such a "hugh" time? What should I need to do?

The investigation:
The #SpringBootApplication annotation is equivalent to the following annotations with default attributes:
#Configuration - Indicates that the class contains one or more #Bean methods. Plays together with #ComponentScan.
#EnableAutoConfiguration - Will attempt to guess and configure beans that you are likely to need. This might cause some performance penalty depending on your application.
#ComponentScan - Configures component scanning. As the package is not defined, scanning will occur from the package of the class with this annotation.
Without more code it is not possible to give you an accurate guess, but I think most of the performance penalty is caused by Spring Boot initialization.

By default the logging level is set to INFO in Spring Boot, probably you changed that?
Add this to application.properties or any other level you need (TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF)
logging.level.root=WARN
As was said before auto configuration may impact performance.
You can play with excluding some configurations that you don't need.
Here is an example (random set - don't follow blindly)
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, SecurityAutoConfiguration.class, SessionAutoConfiguration.class,
ValidationAutoConfiguration.class, ThymeleafAutoConfiguration.class, WebSocketAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, EmbeddedServletContainerAutoConfiguration.class,
JmxAutoConfiguration.class, MultipartAutoConfiguration.class, WebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class, SpringApplicationAdminJmxAutoConfiguration.class,
MessageSourceAutoConfiguration.class, SendGridAutoConfiguration.class, FreeMarkerAutoConfiguration.class,
GroovyTemplateAutoConfiguration.class, DeviceDelegatingViewResolverAutoConfiguration.class,
SitePreferenceAutoConfiguration.class, MustacheAutoConfiguration.class,
PersistenceExceptionTranslationAutoConfiguration.class})
for test classes I would recommend using
#SpringBootTest(classes = TestApplication.class)
also you can exclude Tomcat if you don't need it in your tests
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
and try to give us more details

Related

Prevent SpringSecurity from creating cglib based proxy

I'm using spring-boot 2.3.9 and spring-security with keycloak 12.0.4.
#Configuration
#KeycloakConfiguration
#ConditionalOnProperty("keycloak.enabled")
#Import(KeycloakSpringBootConfigResolver.class)
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = false) // If I exclude this, no cglib based proxy is created, but #Secured is not applied either
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
And my service bean creation config
#Configuration(proxyBeanMethods = false)
public class EnvironmentConfig {
#Bean
// #Scope(proxyMode = ScopedProxyMode.INTERFACES) // Has no effect
MyServiceInterfaceSecured myService() { new ImplA/ImplB/ImplC() }
}
For some reason spring creates cglib based proxies, which causes other issues (final methods...)
09:56:38.828 DEBUG [,] --- [main] m.DelegatingMethodSecurityMetadataSource : Caching method [CacheKey[example.MyServiceImplA; ...] with attributes [...]
09:57:12.992 INFO [,] --- [main] o.s.a.f.CglibAopProxy : Unable to proxy interface-implementing method [public final java.util.List example.MyServiceImplA.sync()] because it is marked as final: Consider using interface-based JDK proxies instead!
09:57:12.992 DEBUG [,] --- [main] o.s.a.f.CglibAopProxy : Final method [public final java.util.List example.MyServiceImplA.sync()] cannot get proxied via CGLIB: Calls to this method will NOT be routed to the target instance and might lead to NPEs against uninitialized fields in the proxy instance.
TLDR: How do I tell spring to create an interface based proxy for my beans instead of cglib based proxies?
(or which bean/code/log can I check to analyze why it tries to use the cglib proxy)
EDIT: This might be related to spring-cloud-starter-sleuth. If I remove that dependency, everything works as expected. But I also need sleuth.
I fixed it by excluding spring-boot-starter-aop from spring-cloud-starter-sleuth.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</exclusion>
</exclusions>
</dependency>
EDIT: I opened an issue asking for the dependency to be optional or an explanation why it is required:
https://github.com/spring-cloud/spring-cloud-sleuth/issues/1933

Sending Email using VSCode (spring-boot-starter-email)

I am implementing how to send email using spring boot
I am trying to implement this in visual studio code.
But it gives me the following error
I added the following two dependencies in my pom.xml for email configuration:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
my main bootstrap class:
#SpringBootApplication
#ComponentScan(basePackages = {
"email"
})
public class Application {
public static void main(String[] args) {
Mail mail = new Mail();
mail.setMailFrom("abc#gmail.com");
mail.setMailTo("xyz#gmail.com");
mail.setMailSubject("Hi");
mail.setMailContent("Hope you are doing well");
ApplicationContext ctx = SpringApplication.run(Application.class,
args);
MailService mailService = (MailService) ctx.getBean("mailService");
mailService.sendEmail(mail);
}
I think my error is related to the #ComponentScan(basePackages = {"email"}) annotation that I have used above
Can anyone help me with the error?
Since we don't know the package structure it is difficult to tell what should be there in the basePackages inside #ComponentScan
Firstly, please move your Application class to one level up in the package structure, so that it reads all packages under it by default and remove the basePackages in component scan. So, it should be just #ComponentScan
That is, if all your classes are in package com.test.mailer then your Application class file should be in com.test
Try this and let us know, also I hope you have the #Service annotation as #Service("mailService")
Update:
Since the user has updated the question later, I am posting the solution that worked for him.
He moved the class one level up and removed the basePackages and it worked for him. As stated in the first part of my answer.
Alternatively, he could have changed #ComponentScan(basePackages = {"email"}) to #ComponentScan("java.risknucleus") in the same structure.

#Value("${local.server.port}") not working in Spring boot 1.5

I am upgrading Spring Boot from 1.3 to 1.5. For upgrading to 1.5 I have replaced
#SpringApplicationConfiguration(classes = TestConfig.class)
#WebIntegrationTest
with
#SpringBootTest(classes = TestConfig.class)
Also, I am using
#Value("${local.server.port}")
protected int port;
to get port number defined in application.properties file. I further use this port number to build a REST URL.
But after the upgrade I am getting the error below whereas the same works fine with 1.3 Spring Boot Test.
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'local.server.port' in value "${local.server.port}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
Am I missing any changes that I need to do for this to work.
You have to provide a value for webEnvironment. In your case DEFINED_PORT like this
#SpringBootTest(classes = App.class, webEnvironment = WebEnvironment.DEFINED_PORT)
public class YourTest {
#LocalServerPort // shorthand for #Value("${local.server.port}")
private Integer port;
...
}
For details see: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications
Adding another alternate solution which I had elsewhere.
I had configured
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
and
#RunWith(SpringRunner.class)
#SpringBootTest(classes = App.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class YourTest {
#LocalServerPort // shorthand for #Value("${local.server.port}")
private Integer port;
...
}
Thinking that was it, and still getting this error even when specifying web environment etc. My ${local.server.port} seemed to be always null.
After some time, I noticed that my Spring Boot startup message contained no notion of the port it was using, so apparently it really didn't listen to any port at all - which explained why it was null in the first place. Adding actual container implementation dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Caused this to appear on my logs:
2019-02-26 18:45:47.231 INFO 12504 --- [ main] o.s.b.web.embedded.jetty.JettyWebServer : Jetty started on port(s) 43132 (http/1.1) with context path '/'
after which local.server.port and #LocalServerPort would also work.
For me the problem was that there was alternative #Configuration class(es) in my other test(s) like this:
#Configuration
public class ReadPropertiesConfiguration {
#Bean
PropertyPlaceholderConfigurer propConfig() {
PropertyPlaceholderConfigurer placeholderConfigurer = new PropertyPlaceholderConfigurer();
placeholderConfigurer.setLocation(new ClassPathResource("application.properties"));
return placeholderConfigurer;
}
}
and #SpringBootApplication of the app was picking that up due to its #ComponentScan, and for some reason it resulted in this problem. When adding exclusion for those and/or replacing them with other solutions things started again to work without problems.
I don't know the root cause why this happens, but that might be your issue as well.
First make sure the property is correctly spelled in the properties file. As i did just few days back using spring-boot-1.5.2 & it works.
Or
You need to add
#PropertySource("classpath:application.properties")
to your class, so it will pick your configurations.
If you need different configurations for test you can add
#TestPropertySource(locations="classpath:test.properties")
Refer #Value not work on Spring Boot Test

How to add log messages in tests (spring boot)?

I want to add logging (for console) in my project, for a test in Spring Boot.
I have my test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class})
public class MyTest {
private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(MyTest.class);
#Autowired
public UserDao userDao;
#Test
public void test1() {
LOGGER.info("info test");
LOGGER.debug("debug test");
}
}
and my test config:
#Configuration
#EnableJpaRepositories("example.dao")
#ComponentScan(basePackageClasses = { MyServiceImpl.class})
#EntityScan({"example.model"})
#Import({DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class TestConfig {
}
I created an application.properties file in test/resource. Gradle sees my resource folder as a resource for tests.
Here's my application.properties:
logging.level.= INFO
logging.level.tests.= INFO
logging.level.org.hibernate= INFO
logging.level.org.springframework= INFO
logging.level.org.apache.cxf= INFO
But when I run my test, I have:
16:59:17.593 [main] INFO tests.MyTest - info test
16:59:17.594 [main] DEBUG tests.MyTest - debug test
in the console. Why?
I set just 'INFO'(logging.level.= INFO). Why is 'DEBUG' in the console? How can to set it just to'INFO'?
It's a two step proccess.
First, add spring-boot-starter-test as a test dependency. Then tell JUnit to use the ContextLoader from the just added dependency.
Change
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class})
public class MyTest {
...
to
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class},
loader = SpringApplicationContextLoader.class)
public class MyTest {
...
The context loader lives in spring-boot-starter-test added in the first step and does the initialization magic normally done by the ApplicationBootstrapper.
Depending on the spring-boot version you are using there are some other possibilities (e.g. using #SpringApplicationConfiguration in place of #ContextConfiguration). You can read more about that in this spring blog: https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
Try adding #SpringBootConfiguration instead of #ContextConfiguration on your MyTest class.
Follow the example in the Reference doc:
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications
A Spring Boot application is just a Spring ApplicationContext so nothing very special has to be done to test it beyond what you would normally do with a vanilla Spring context. One thing to watch out for though is that the external properties, logging and other features of Spring Boot are only installed in the context by default if you use SpringApplication to create it.

Can I intercept Spring #Autowired process to do validation checks?

Sometimes we make mistakes in our code and #Autowired a prototype-scoped bean into a singleton-scoped bean. This is of course wrong because then the singleton is probably going to use that dependency as if it was also a singleton.
Is there any way of intercepting the autowiring/DI process to detect this and raise an error ? This would be for detection at development time.
The best way to achieve this is through your unit tests. For example:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyAppConfig.class, loader = AnnotationConfigContextLoader.class)
public class MyServiceTest {
#Autowired(required = true)
MyService myService;
#Test
public void shouldAutowire() {}
}
The #ContextConfiguration can be used with Java config as above, or it can refer to XML config files. By doing this, Spring will be used to inject all of your dependencies whenever you run your tests. By including "required = true" on your #Autowired beans, you are ensuring that Spring will throw an exception during that phase, and your test will fail. The example above may not look fancy, but it will ensure that any configuration errors are caught. Of course, you can go further and have your tests make use of the injected beans. I find that rather handy for database access integration tests.
This is not intercepting the autowiring process itself, but you can of course test that your beans are behaving correctly.
You will need to import the spring-test dependency. i.e. For Maven:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>

Categories