I've found a useful article that explains how to make Jersey to use SLF4J instead of JUL. Now my unit test looks like (and it works perfectly):
public class FooTest extends JerseyTest {
#BeforeClass
public static void initLogger() {
java.util.logging.Logger rootLogger =
java.util.logging.LogManager.getLogManager().getLogger("");
java.util.logging.Handler[] handlers = rootLogger.getHandlers();
for (int i = 0; i < handlers.length; i++) {
rootLogger.removeHandler(handlers[i]);
}
org.slf4j.bridge.SLF4JBridgeHandler.install();
}
public FooTest() {
super("com.XXX");
}
#Test
public void testSomething() throws Exception {
// ...
}
}
My pom.xml includes these dependencies:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
It works perfectly, but I don't want to make the same configuration in every unit test. It's an obvious code duplication, which I would like to avoid. How can I do this more effectively?
ps. Maybe it's not possible to optimize the code above and I'm doing the best I can?
If you are using the client API you can manually redirect the logs to slf4j (note that it may break in future versions although it seems unlikely):
Logger LOG = LoggerFactory.getLogger(MyClass.class); //slf4j logger
WebTarget ws = ClientBuilder.newClient(config)
.register(new LoggingFilter(new JulFacade(), true));
private static class JulFacade extends java.util.logging.Logger {
JulFacade() { super("Jersey", null); }
#Override public void info(String msg) { LOG.info(msg); }
}
The best way to do it is through a custom Listener. Being initialized before JSF servlet it should configure jul-to-slf4j bridge in contextInitialized(ServletContextEvent).
This worked for me:
public abstract class JerseyTestSFL4J extends JerseyTest {
static {
// Get JerseyTest to use SLF4J instead of JUL
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}
and then having my tests extend JerseyTestSFL4J.
What it sounds like is you'd want the JUL/SLF4J configuration handle before JUnit starts testing so it could be covered for all tests? Here's a way you could do that.
Output
MySuite.init()
MySuite()
getSuiteTests()
MyTest.init()
MyTest()
test()
Code
#RunWith(AbstractTestSuite.TestSuiteRunner.class)
public abstract class AbstractTestSuite {
public static class TestSuiteRunner extends Suite {
public TestSuiteRunner(Class<?> klass) throws Exception {
super(klass, ((Class<? extends AbstractTestSuite>) klass).newInstance().getSuiteClasses());
}
}
public Class<?>[] getSuiteClasses() {
List<Class<?>> all = new ArrayList<Class<?>>();
for (Class<?> testClass : getSuiteTests()) {
all.add(testClass);
}
return all.toArray(new Class<?>[0]);
}
protected abstract Iterable<Class<?>> getSuiteTests();
}
public class MySuite extends AbstractTestSuite {
public static class MyTest {
static {
System.out.println("MyTest.init()");
}
public MyTest() {
System.out.println("MyTest()");
}
#Test
public void test() {
System.out.println("test()");
assertTrue(true);
}
}
static {
System.out.println("MySuite.init()");
}
public MySuite() {
System.out.println("MySuite()");
}
#Override
protected Iterable<Class<?>> getSuiteTests() {
System.out.println("getSuiteTests()");
return Arrays.asList(new Class<?>[] {MyTest.class});
}
}
In my app Jersey logging (with proper pom.xml and logback.xml) works fine only with
SLF4JBridgeHandler.install();
For tests you can make base abstract test with common configuration and extends other JUnit from it:
public abstract class AbstractTest {
private static final Logger LOG = LoggerFactory.getLogger(AbstractTest.class);
static {
SLF4JBridgeHandler.install();
}
#Rule
public ExpectedException thrown = ExpectedException.none();
#Rule
// http://stackoverflow.com/questions/14892125/what-is-the-best-practice-to-determine-the-execution-time-of-the-bussiness-relev
public Stopwatch stopwatch = new Stopwatch() {
#Override
protected void finished(long nanos, Description description) {
...
Slf4jLogger from org.apache.cxf:cxf-core package is another option. It implements java.util.logging.Logger and delegate cals to slf4J.
Jersey server:
ResourceConfig config = new ResourceConfig(HelloWorldResource.class);
config.register(
new Slf4jLogger(this.getClass().getName(), null));
Jersey client:
ClientBuilder
.newClient()
.register(
new LoggingFeature(
new Slf4jLogger(this.getClass().getName(), null)));
Related
I have an issue in writing a unit test for a function which has static method of another class.
public class ClassA{
private static LoadingCache<String, String> someMap;
public static void init() {
if (someMap == null) {
someMap = CacheBuilder.newBuilder()
.concurrencyLevel(10)
.maximumSize(10) // Maximum of 10 records can be cached
.build(new CacheLoader<String, String>() {
#Override
public String load(String key) {
return "myName";
}
});
}
}
public static String getName() {
try {
return someMap.get("key");
} catch (ExecutionException ex) {
LOGGER.error("Error {}", ex.getMessage());
}
return null;
}
Trying to test the following class and it's method:
public class ClassB{
public String method(){
final String name = ClassA.getName();
return "Hello: " + name;
}
}
I am calling ClassA.init() at the start of the service in dropwizard framework. So, I think someMap gets initialized that way and all resources can directly use ClassA.getName().
But, when I try to test this, I am getting NPE because someMap is not initialized.
How can I simply do:
when(ClassB.getName()).thenReturn("myName");
Based on the comments:
I added the following to the POM:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
// test function looks as follows:
#Test
public void test(){
mockStatic(ClassA.class);
when(ClassA.getName()).thenReturn("myName");
// do the rest
}
It works!
I'm feeling stupid to ask this, but I can't understand where I'm wrong with my code.
The context is :
a Spring Boot application (1.5.7) with an embedded Jetty server and a
controller to expose some endpoints
a unique #Configuration class, where some of my beans are defined (Singleton and Prototype scopes)
a #Service that uses some beans defined in my #Configuration class
The problem is:
a NoSuchBeanDefinitionException for one of my #Configuration bean.
Now the details:
My SpringBootApplication :
#SpringBootApplication
public class HbbTVApplication {
public static void main(String[] args) {
SpringApplication.run(HbbTVApplication.class, args);
}
}
My #Configuration class:
#Configuration
#Profile(value = { "dev", "int", "pre", "pro" })
public class StandaloneFrontalConfig extends WebMvcConfigurerAdapter {
#Value("${kafka.bootstrap-servers}")
private String bootstrapServers;
#Bean
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return props;
}
#Bean
public ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
#Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/standalone/");
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*");
}
};
}
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
#Bean
public Security securityManager() {
return new Security();
}
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KngAflow getTechnicalCookie() {
return new KngAflow();
}
#Bean
public EmbeddedServletContainerCustomizer customizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof JettyEmbeddedServletContainerFactory) {
customizeJetty((JettyEmbeddedServletContainerFactory) container);
}
}
private void customizeJetty(JettyEmbeddedServletContainerFactory jetty) {
jetty.addServerCustomizers(new JettyServerCustomizer() {
#Override
public void customize(Server server) {
for (Connector connector : server.getConnectors()) {
if (connector instanceof ServerConnector) {
HttpConnectionFactory connectionFactory = ((ServerConnector) connector)
.getConnectionFactory(HttpConnectionFactory.class);
connectionFactory.getHttpConfiguration().setCookieCompliance(CookieCompliance.RFC2965);
}
}
}
});
}
};
}
}
My #Service:
#Service
public class CookieService implements services.CookieService, InitializingBean {
/**
* Serializable
*/
private static final long serialVersionUID = -1997257884335775587L;
#Autowired
ApplicationContext app;
#Override
public Cookie createTechnicalCookie() {
return new Cookie(app.getBean(KngAflow.class), null);
}
#Override
public void afterPropertiesSet() throws Exception {
if (app != null) {
for (String bean : app.getBeanDefinitionNames()) {
System.out.println("Bean: " + bean);
}
}
}
}
And the "non defined" bean:
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties({ "security", "maxAge", "domain", "updated" })
public class KngAflow implements Serializable, InitializingBean {
#JsonProperty(value = "did")
private String did;
#JsonProperty(value = "checksum")
private String checksum;
#Autowired
private Security security;
private Integer maxAge;
private String domain;
private boolean updated = false;
public KngAflow() {
domain = ".mydomain.com";
}
#Override
public void afterPropertiesSet() throws Exception {
did = UUID.randomUUID().toString();
maxAge = 365 * 24 * 60 * 60;
checksum = security.encrypt(did + security.md5(did));
}
}
NB: Classes are not complete, and there are more classes in my project. I only put what I saw as relevant information.
If something else is needed, just ask me please.
By the way, all the endpoints are defined into a unique #Controller class, and all the endpoints are working except those needing the getTechCookie #Bean.
So, my problem occurs in runtime execution. When I start my Spring Boot app, Jetty is started and listening on the configured port.
Nevertheless, if you look at the CookieService #Service, I'm listing all the bean names defined in the autowired context and my getTechnicalCookie (a.k.a KngAflow) #Bean is missing. I can't understand why.
Of course, when I invoke my #controller to execute my #Service code, the NoSuchBeanDefinitionException is thrown executing the line app.getBean(KngAflow.class).
I tried to use a bean name instead of bean type, no change.
For testing purpose (as it doesn't make sense from a logical point of view), I defined my bean getTechCookie #Bean as a Singleton scoped bean, and the name is still missing from the ApplicationContext.
And the last but not least thing is: Everything works fine with Eclipse!
I mean, all my devs are done using Eclipse IDE. My Spring Boot app is built with Maven and executing it inside Eclipse works correctly (and my getTechCookie Bean is defined and listed).
When I package my app using the Maven Spring Boot plugin and execute it using java -jar, my getTechCookie (KngAflow.class) bean is missing. Nevertheless, this class is present inside the jar.
Spring parameters to launch the spring boot app are spring default values (port 8080, no SSL, ...) and the active.profiles are always between dev, int, pre or pro (those defined in my #Configuration class)
What am I doing wrong?
Thanks!
If it helps, I add my POM definition:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.mydomain.bigdata</groupId>
<artifactId>mybigapp</artifactId>
<version>1.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
<include>application.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
EDIT: I changed my #Service class to "force" spring to accept my class as a prototype bean, and it works. It's very ugly but it works. But if someone could help me to find what's wrong, I don't like this workaround:
#Override
public void afterPropertiesSet() throws Exception {
if (!context.containsBeanDefinition(KngAflow.class.getName()))
context.registerBeanDefinition(KngAflow.class.getName(),
BeanDefinitionBuilder.genericBeanDefinition(KngAflow.class).setScope("prototype").getBeanDefinition());
}
I made a following simple application to reproduce issue.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
run(Application.class, args);
}
}
#Configuration
#Profile("dev")
public class BeanConfiguration {
#Bean
#Scope(scopeName = SCOPE_PROTOTYPE)
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
}
public class PrototypeBean {}
#Service
#Slf4j
public class SingletonBean implements InitializingBean {
#Autowired
private ApplicationContext context;
public PrototypeBean getPrototypeBean() {
return context.getBean(PrototypeBean.class);
}
#Override
public void afterPropertiesSet() throws Exception {
for (String name : context.getBeanDefinitionNames()) {
Class<?> c = context.getBean(name).getClass();
log.debug("===> Name: {}, Type = {}", name, c.getTypeName());
}
}
}
#RestController
#RequestMapping("/bean")
public class BeanRestController {
#Autowired
private SingletonBean singletonBean;
#GetMapping("/name")
public String getName() {
return singletonBean.getPrototypeBean().getClass().getName();
}
}
When I execute application with -Dspring.profiles.active=dev setting
Then I see in the log without no issue and REST endpoint gives back response properly:
===> Name: prototypeBean, Type = PrototypeBean
But if I execute application without profile setting
Then I see error in the log and REST endpoint raise exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'PrototypeBean' available
I have the below FeignClient:
#FeignClient(name="FooMS",fallback=CustomerFeign.CustomerFeignImpl.class)
public interface CustomerFeign {
#RequestMapping(value="/bar/{phoneNo}")
List<Long> getFriends(#PathVariable("phoneNo") Long phoneNo);
class CustomerFeignImpl implements CustomerFeign{
#Override
public List<Long> getFriends(Long phoneNo) {
return new ArrayList<Long>(108);
}
}
}
When the FooMS instance is down, I get a 500 error instead of the fallback being executed. Why is this happening?
adding #Component and feign.hystrix.enabled=true works fine
Tag your CustomerFeignImpl as a #Component or create a #Bean out of it.
This works for me with 2020.0.3:
In application.properties
feign.circuitbreaker.enabled=true
In pom.xml
<spring-cloud.version>2020.0.3</spring-cloud.version>
and
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
Thank you, rostlvan!
I am outlining my implementation below:
I am using Spring Cloud version 2020.0.4 and the following configuration worked for me:
in pom.xml, I have these dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
Though I'm not sure if we need to have both openfeign and hystrix dependencies. Someone can validate that!
In my application.properties I have feign.circuitbreaker.enabled=true
In my Main Application class, I have
#SpringBootApplication
#EnableFeignClients
public class MySpringBootApplication{
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
}
And finally, my Feign Client, fallback and fallback factory:
UserServiceFeignClient.java
#FeignClient(name = "USER-SERVICE", fallbackFactory = UserServiceFallbackFactory.class)
public interface UserServiceFeignClient {
#GetMapping("/api/users/{userId}")
public ResponseEntity<User> getUser(#PathVariable String userId);
}
UserServiceFeignClientFallback.java
public class UserServiceFeignClientFallback implements UserServiceFeignClient{
#Override
public ResponseEntity<User> getUser(String userId) {
return ResponseEntity.ok().body(new User());
}
}
And, UserServiceFeignClientFallbackFactory.java:
#Component
public class UserServiceFallbackFactory implements FallbackFactory<UserServiceFeignClientFallback>{
#Override
public UserServiceFeignClientFallback create(Throwable cause) {
return new UserServiceFeignClientFallback();
}
}
Was facing the problem myself, until I stumbled upon the answer from #rostlvan
So, I have the next class:
public class MyCustomClass {
private String someField;
public String getSomeField() {
return someField;
}
public void setSomeField(String someField) {
this.someField = someField;
}
}
I would like to handle methods that return an instance of this class:
#Aspect
public class CustomAspect {
#AfterReturning(
pointcut = "execution(* com.*.*(..))",
returning = "val")
public void handleCustom(JoinPoint joinPoint, MyCustomClass val) {
System.out.println(val.getSomeField());
}
}
But it's working only if the type of val is Object:
public void handleCustom(JoinPoint joinPoint, Object val) {//...}
I tried to make something like this:
if (val instanceof MyCustomClass) {
System.out.println(((MyCustomClass) val).getSomeField());
}
But it doesn't work, in debug mode I saw that the type is ArrayList and it's empty.
I know, I'm a beginner in AOP, but Could you please advise some workarounds that could help me?
updated:
Maven dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
AspectConfig (imported to the Application class):
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages="com.aspect")
public class AspectConfig {
#Bean
public CustomAspect customAspect () {
return new CustomAspect ();
}
}
I tried to use CustomAspect as a #Component as well.
The custom object is just a DTO that is taken from maven artifact and is obtained via rest request.
So, I DO NOT have problems if the type of val is Object, everything works fine. But I do want to have MyCustomClass there instead of Object. Is it possible?
update 2:
#RestController
#RequestMapping(value = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyController{
#Autowired
private MyService myService;
#RequestMapping(value = "dosmth")
public ResponseEntity<?> doSmth() {
SomeObject res = myService.doSmthAndGet();
return new ResponseEntity<>(res, HttpStatus.OK);
}
}
#Service
public class MyService{
public SomeObject doSmthAndGet() {
return SomeObject.of(getMyCustomClass());
}
private MyCustomClass getMyCustomClass() {
MyCustomClass result = new MyCustomClass();
result.setSomeField("Hello, Stack!");
return result;
}
}
It seems that #AfterReturning doesn't work for my submethod:
private MyCustomClass getMyCustomClass()
It's only triggered by the method that was called from Controller:
public SomeObject doSmthAndGet()
I tried to use different executions for #AfterReturning...
What am I doing wrong?
I have this Spring MVC controller:
#Controller
#RequestMapping(value = "/foo")
public class FooController {
#Inject
private FooService fooService;
#RequestMapping(value = "foo/new")
public final String add(final ModelMap model) {
model.addAttribute(fooService.createFoo());
return "foo/detail";
}
#RequestMapping(value = "foo/{id}")
public final String detail(final ModelMap model, #PathVariable long id) {
model.addAttribute(fooService.findById(id));
return "foo/detail";
}
#RequestMapping(value="foo/update", method=RequestMethod.POST)
public final String save(#Valid #ModelAttribute final Foo foo, final BindingResult result, final SessionStatus status,
final RedirectAttributes ra, final HttpServletRequest request) {
if (result.hasErrors()) {
return "foo/detail";
}
fooService.save(foo);
status.setComplete();
Message.success(ra, "message.ok");
return "redirect:foo/list";
}
#RequestMapping( value= "/foo/delete/{id}", method=RequestMethod.POST)
public String delete(#PathVariable final Long id, final SessionStatus status, final RedirectAttributes ra, final HttpServletRequest request){
if (fooService.findByIdWithOtherFoos(id).getOtherFoos().isEmpty()) {
fooService.delete(id);
status.setComplete();
MessageHelper.success(ra, "message.sucess");
} else {
Message.error(ra, "message.error");
}
return "redirect:foo/list";
}
}
And this Service:
#Service
#Transactional(readOnly = true)
public class FooServiceImpl implements FooService {
#Inject
private fooRepository fooRepo;
#Override
public final Foo createFoo() {
return new Foo();
}
#Override
#Transactional(readOnly = false)
public final void save(final Foo foo) {
if (foo.getId() == null) {
foo.setDate(new Date());
}
fooRepo.save(foo);
}
#Override
#Transactional(readOnly = false)
public final void delete(final Long id) {
fooRepo.delete(id);
}
#Override
public final Foo findById(final Long id) {
return fooRepo.findOne(id);
}
#Override
public Foo findByIdWithOtherFoos(Long id) {
Foo foo = fooRepo.findOne(id);
Hibernate.initialize(foo.getOtherFoos());
return foo;
}
#Override
public final Page<Foo> findAll(final Pageable pageable) {
return fooRepo.findAll(pageable);
}
#Override
public final Page<Foo> find(final String filter, final Pageable pageable) {
// TODO Auto-generated method stub
return null;
}
#Override
public final List<Foo> findAll(final Sort sort) {
return fooRepo.findAll(sort);
}
}
What is the best way of testing with JUnit drivers and services to cover all logical conditions?
I always end up with a bunch of test lines to cover all logical conditions.
We recommend using MockitoJUnitRunner? Or create classes which create configuration beans. And charge them with ContextConfiguration 'ContextConfiguration (FooServiceImplTestConfiguration.class classes = {})'
How to implement the Given-When-Then pattern?
When it comes to testing Controllers (especially integration testing) i suggest using Spring's MockMVC or Rest-Assured. And example of using Rest-Assured in action can be seen below:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = SomeApplication.class)
#WebIntegrationTest(randomPort = true)
#ActiveProfiles(profiles = "test")
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class SomeControllerTest {
#Test
public void getAllSomeObjects() {
expect().statusCode(HttpStatus.SC_OK)
.body("", hasSize(2))
.body("[0]", notNullValue())
.body("[1]", notNullValue())
.body("findAll { it.name.equals('TEST1') }", hasSize(1))
.body("findAll { it.name.equals('TEST2') }", hasSize(1))
.when()
.get("/someAddress");
}
}
For testing Services i suggest using Mockito. Additionally Hamcrest Matchers is a useful library for assertions in tests. Example of using both below:
public class SomeServiceTest {
#InjectMocks
private SomeService someService;
#Mock
private SomeInnerService someInnerService;
#Before
public void setUp() {
initMocks(this);
Mockito.when(someInnerService.useMethod("argument")).thenReturn(new SomeObject());
}
#Test
public void testSomeMethod() {
Set<SomeObject> someObjects= someService.someMethod();
assertThat(someObjects, is(notNullValue()));
assertThat(someObjects, is(hasSize(4)));
}
}
You should test both independently.
First create a unit test for your service. You can use Mockito to mock your service dependency as fooRepository.
#Test
public void testFindById() {
when(fooServices.findById(123)).thenReturn(fooSample);
assertThat(what you want);
}
Then, you should create an other unit test for your controller. The easiest way to do that is to use MockMvc provided in spring-test. And in this case, you can use Mockito to mock fooService.
Best Part. Use spring MVC test layer. As they are providing their own API's which helps you to mock controllers and provide you session objects which you can fill with required state. you can find lots of examples online.
http://www.petrikainulainen.net/spring-mvc-test-tutorial/
You can actually test all your layers seperately .. All the best !!
Have a look at Spring-Test-MVC. That's a framework for exactly that purpose and comes with a lot of easy to understand and rebuild examples.
Personally I add Mockito / PowerMock to the mix for mocking internal dependencies away.
Good luck.
It depends on what kind of test you want to implement.
Certainly Spring Test helps in this. This module supports "unit" and integration testing. Note that unit tests are not really true unit tests because there is a little bit of context loading involved while using Spring Test at it's minimum.
Check the MockMvc class that you can use to make requests to controllers.
I think the best way is to use ContextConfiguration in combination with DirtiesContext, MockMvcBuilders and Mockito. This gives you the advantage of creating a Spring controller through an application context and injecting beans whose behaviour is defined through Mockito. In this case you can reach high line and condition coverage. Here is an example for your code:
#ContextConfiguration
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#RunWith(SpringJUnit4ClassRunner.class)
public class FooControllerTest {
private MockMvc mockMvc;
#Autowired
private FooService service;
#Autowired
private FooController controller;
#Before
public void initController() {
mockMvc = MockMvcBuilders.standaloneSetup(frontEndController).build();
}
#Test
public void shouldDoSomeThing_CornerCase() {
// Given:
// define the behaviour of service with when(service...)
// Then:
// perform a request on contoller
mockMvc.perform(get("/foo/delete/{id}"))
// When:
// user Mockito verify
// or
// MockMvcRequestBuilders
}
#Configuration
public static class FooConfiguration {
#Bean
public FooController controller() {
return new FooController();
}
#Bean
public FooService service() {
return mock(FooService.class);
}
}
}
DirtiesContext is important so that you get clean mocks a every test.
Finally I use this solution.
For my Domain Model I use this link http://www.javacodegeeks.com/2014/09/tips-for-unit-testing-javabeans.html
/**
* #param <T>
*/
public abstract class AbstractJavaBeanTest<T> {
protected String[] propertiesToBeIgnored;
protected abstract T getBeanInstance();
#Test
public void beanIsSerializable() throws Exception {
final T myBean = getBeanInstance();
final byte[] serializedMyBean = SerializationUtils.serialize((Serializable) myBean);
#SuppressWarnings("unchecked")
final T deserializedMyBean = (T) SerializationUtils.deserialize(serializedMyBean);
assertEquals(myBean, deserializedMyBean);
}
#Test
public void equalsAndHashCodeContract() {
EqualsVerifier.forClass(getBeanInstance().getClass()).suppress(Warning.STRICT_INHERITANCE, Warning.NONFINAL_FIELDS).verify();
}
#Test
public void getterAndSetterCorrectness() throws Exception {
final BeanTester beanTester = new BeanTester();
beanTester.getFactoryCollection().addFactory(LocalDateTime.class, new LocalDateTimeFactory());
beanTester.testBean(getBeanInstance().getClass());
}
class LocalDateTimeFactory implements Factory {
#Override
public LocalDateTime create() {
return LocalDateTime.now();
}
}
}
/**
* Test Foo
*/
public class FooTest extends AbstractJavaBeanTest<Foo> {
#Override
protected Foo getBeanInstance() {
return new Foo();
}
}
I add this dependencies to pom.xml:
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>1.7.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.meanbean</groupId>
<artifactId>meanbean</artifactId>
<version>2.0.3</version>
</dependency>
For my MVC controllers I use this link http://www.luckyryan.com/2013/08/24/unit-test-controllers-spring-mvc-test/
/**
* Test FooController
*/
public class FooControllerTest {
#Mock
private FooService fooService;
#InjectMocks
private FooController fooController;
private MockMvc mockMvc;
#Before
public void setup() {
// Process mock annotations
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(fooController).build();
}
#Test
public void testAdd() throws Exception {
Foo foo = new Foo();
// given
given(FooService.createFoo()).willReturn(foo);
// when
// then
this.mockMvc.perform(get("/foo/new"))
.andExpect(forwardedUrl("foo/detail"))
.andExpect(model().attributeExists("foo"))
.andExpect(model().attribute("foo", is(foo)));
}
#Test
public void testDetail() throws Exception {
Foo foo = new Foo();
Long fooId = 1L;
// given
given(fooService.findById(fooId)).willReturn(foo);
// when
// then
this.mockMvc.perform(get("/foo/" + fooId))
.andExpect(forwardedUrl("foo/detail"))
.andExpect(view().name("foo/detail"))
.andExpect(model().attributeExists("foo"))
.andExpect(model().attribute("foo", is(foo)));
}
#Test
public void testSave() throws Exception {
Foo foo = new Foo();
// given
// when
// then
//With form errors
this.mockMvc.perform(post("/foo/update")
.param("name", "")
.sessionAttr("foo", foo))
.andExpect(forwardedUrl("foo/detail"))
.andExpect(model().hasErrors())
.andExpect(model().attributeHasFieldErrors("foo", "name"));
//Without form errores
this.mockMvc.perform(post("/foo/update")
.param("name", "nameValue")
.param("code", "codeValue")
.param("date", "20/10/2015")
.requestAttr("referer", "/foo/list")
.sessionAttr("foo", foo))
.andExpect(view().name("redirect:" + "/foo/list"))
.andExpect(model().hasNoErrors())
.andExpect(flash().attributeExists("message"))
.andExpect(flash().attribute("message", hasProperty("message", is("message.ok"))))
.andExpect(flash().attribute("message", hasProperty("type", is(Message.Type.SUCCESS))))
.andExpect(status().isFound());
}
#Test
public void testDelete() throws Exception {
Foo foo = new Foo();
foo.setOtherFoos(new ArrayList<OtherFoo>());
Long fooId = 1L;
// given
given(fooService.findByIdWithOtherFoos(fooId)).willReturn(foo);
// when
// then
//Without errors: without other foos
this.mockMvc.perform(post("/foo/delete/" + fooId)
.sessionAttr("foo", foo)
.requestAttr("referer", "/foo/list"))
.andExpect(view().name("redirect:" + "/foo/list"))
.andExpect(flash().attributeExists("message"))
.andExpect(flash().attribute("message", hasProperty("message", is("message.ok"))))
.andExpect(flash().attribute("message", hasProperty("type", is(Message.Type.SUCCESS))));
// given
foo.getOtherFoos().add(new OtherFoo());
given(fooService.findByIdWithOtherFoos(fooId)).willReturn(foo);
// when
// then
//With errors: with other foos
this.mockMvc.perform(post("/foo/delete/" + fooId)
.sessionAttr("foo", foo)
.requestAttr("referer", "/foo/list"))
.andExpect(view().name("redirect:" + "/foo/list"))
.andExpect(flash().attributeExists("message"))
.andExpect(flash().attribute("message", hasProperty("message", is("message.error"))))
.andExpect(flash().attribute("message", hasProperty("type", is(Message.Type.DANGER))));
}
}
For my JUnit service test I implemented a class for Configuration and I load it in the service test
#Configuration
public class FooServiceImplTestConfiguration {
#Bean
public FooService fooService() {
return new FooServiceImpl();
}
#Bean
public FooRepository fooRepository() {
return Mockito.mock(FooRepository.class);
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {FooServiceImplTestConfiguration.class})
public class FooServiceImplTest {
#Inject
private FooRepository fooRepository;;
#Inject
private FooService fooService;
#BeforeClass
public static void oneTimeSetUp() {
// one-time initialization code
System.out.println("#BeforeClass - oneTimeSetUp");
}
#AfterClass
public static void oneTimeTearDown() {
// one-time cleanup code
System.out.println("#AfterClass - oneTimeTearDown");
}
#Before
public void setUp() {
}
#After
public void tearDown() {
}
#Test
public void createFoo() {
assertNotNull(fooService.createFoo());
}
#Test
public void save() {
//New foo
Foo saveFoo = new Foo();
// given
// when
fooService.save(saveFoo);
// then
assertNotNull(saveFoo.getDate());
saveFoo.setId(1L);
Date date = new Date();
saveFoo.setDate(date);
// given
//when
fooService.save(saveFoo);
//then
assertThat(date, is(saveFoo.getDate()));
}
#Test
public void delete() {
//given
//when
fooService.deleteFoo(Matchers.anyLong());
//then
}
#Test
public void findById() {
Long id = 1L;
Foo fooResult = new Foo();
//given
given(fooRepository.findOne(id)).willReturn(fooResult);
//when
Foo foo = fooService.findById(id);
//then
assertThat(foo, is(fooResult));
}
#Test
public void findByIdWithOtherFoos() {
Long id = 1L;
Foo fooResult = new Foo();
//given
given(fooRepository.findOne(id)).willReturn(fooResult);
//when
Foo foo = fooService.findByIdWithOtherFoos(id);
//then
assertThat(foo, is(fooResult));
}
#Test
public void findAll() {
Page<Foo> fooResult = new PageImpl<>(new ArrayList<Foo>());
given(fooRepository.findAll(Matchers.<Pageable>anyObject())).willReturn(fooResult);
//when
Page<Foo> foos = fooService.findAll(Matchers.<Pageable>anyObject());
//then
assertThat(foos, is(fooResult));
}
#Test
public void findAllList() {
List<Foo> fooResult = new ArrayList<Foo>();
given(fooRepository.findAll(Matchers.<Sort>anyObject())).willReturn(fooResult);
//when
List<Foo> foos = fooService.findAll(Matchers.<Sort>anyObject());
//then
assertThat(foos, is(fooResult));
}
}
In my pom I need add this dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<!-- This is for mocking the service -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
I need change the my hibernate validator version for this:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
I need add this dependencies too because I got this excecption:
Cause: java.lang.AbstractMethodError:org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
Detail message: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
</dependency>
I'm using spring data, I need to do the test for my custom CrudRepositories too.