[JUnit Unit test cases]: java.lang.NoClassDefFoundError: org/apache/commons/logging/Log - spring

Getting java.lang.NoClassDefFoundError: org/apache/commons/logging/Log as I mock. Refer code
#Mock
private RestTemplate restTemplate;
Dependencies we included
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.5"
testCompile "org.apache.logging.log4j:log4j-core:2.5"
testCompile "org.slf4j:jcl-over-slf4j:1.7.21"
testCompile "commons-logging:commons-logging:1.1.1"
Note: We are using slf4j logging.
For actual application these dependencies are resolved by tomcat server.

First thing you need to keep in mind is -- "you are using a mock object" and mock is just a place holder, it is not real object. So, you have to define its behavior and dependencies. the template is defined as mock, so you have to inject any dependent objects like logger. you can achieve that by using something like this
#RunWith(MockitoJUnitRunner.class)
public class MyTest {
#Mock
Logger logger;
#InjectMocks
private RestTemplate restTemplate;
#Test
public void isLoggerGettingCalled() throws Exception {
// Your test logic
}
}

I think this is one of those rare cases where you do not actually miss the specific class, but have one to many of them. If you check these two jars that you imported:
testCompile "org.slf4j:jcl-over-slf4j:1.7.21"
testCompile "commons-logging:commons-logging:1.1.1"
You will see, that both of them have the following class in them:
org/apache/commons/logging/Log
The classloader encountered this duplication and could not load the class definition. If you are looking for the right combination of slf4j jars, I would go with either of these two options:
slf4j-api-[latest-version].jar
slf4j-simple-[latest-version].jar
OR
slf4j-api-[latest-version].jar
slf4j-log4j12-[latest-version].jar
log4j-[latest-version].jar
But in the end there are many combinations to choose from depending on your preference.

Related

Springboot Webflux endpoint not found by test - what is causing it?

I have the following code, and am consistently receiving 404 not found errors? Any advice would be much appreciated!
I've researched conflicting dependencies which does not seem to be the problem. I've also ensured that I am returning the correct content type.
One thing that I am not sure I'm doing correctly is annotating with the Bean and Autowired annotations. I have little understanding of what those do at the moment.
Router class below
#Configuration
#AllArgsConstructor
public class AgencyRouter {
#Bean
public RouterFunction<ServerResponse> agencyRoutes(AgencyController agencyController) {
return RouterFunctions
.route(RequestPredicates.POST("/agency").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), agencyController::createAgency);
}
}
Controller/Handler class below
#Component
public class AgencyController {
public Mono<ServerResponse> createAgency(ServerRequest request){
return ServerResponse.ok()
// .contentType(MediaType.APPLICATION_JSON)
.body(
Flux.just("Test", "message")
.delayElements(Duration.ofSeconds(1)).log(), String.class
);
}
}
Test class
#AutoConfigureWebTestClient
public class AgencyControllerTest {
#Autowired
private WebTestClient webClient;
#Test
void testCreateAgency() {
AgencyRequest request = new AgencyRequest(new AgencyRequestFields("TestName", true));
webClient.post()
.uri("/agency")
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(request))
.exchange()
.expectStatus().is2xxSuccessful();
}
}
build.gradle dependencies
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
//implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.flywaydb:flyway-core'
implementation 'org.springdoc:springdoc-openapi-webflux-ui:1.5.2'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'
}
Thanks in advance!!!
The easiest way is just to add #SpringBootTest on your AgencyControllerTest class (along with #AutoConfigureWebTestClient which you already have):
#SpringBootTest
#AutoConfigureWebTestClient
public class AgencyControllerTest {
...which sets up full auto-configuration along with your web test client. Then you don't need to do anything else - all your controllers, routes etc. will be available. This is probably easiest to remember if you're just getting started.
Alternatively, you can use #WebFluxTest and #ContextConfiguration to just instantiate the context you need for this particular test:
#WebFluxTest
#AutoConfigureWebTestClient
#ContextConfiguration(classes = {AgencyController.class, AgencyRouter.class})
public class AgencyControllerTest {
If you have other routes, controllers, beans, etc. set up and only need a small subset then this approach is more efficient, as you're not setting up and tearing down the entire context for each test (only what you need.)
One thing that I am not sure I'm doing correctly is annotating with the Bean and Autowired annotations. I have little understanding of what those do at the moment.
I'd recommend taking a good look at dependency injection & inversion of control (both theoretically and in a Spring context) - this is pretty much the foundation of Spring, and you'll almost certainly come unstuck at some point unless you have (at least) a base level understanding of this.

Logger field injection and unit tests

I have the following piece of code:
#Component
public class MyBean {
#Inject
private Logger logger;
private Service service;
#Inject
public MyBean(Service service) {
this.service = service;
}
}
I used constructor injection in order to have clear list of dependencies required by MyBean, but I have also decided to use field injection for logger (since most the the classes would require this extra parameter in constructor, which just feels not right).
I want to use Mockito for testing and I have the following options to use:
#InjectMocks
I have read in multiple places to avoid it
I can't configure mock before my class is initialized
Inject logger with Whitebox
use of reflection
Use SpringRunner
is it really necessary to create Spring context just to instantiate one class for unit test?
Is there any cleaner way to accomplish that?

Spring: mocking security enhanced service

We'are imlementing part of our security at service layer, so I add #PreAuthorize annotation to some methods of MyService.
At MyServiceSecurityTest I want to test only security role-permission matrix, without any business logic. For that reason I have to mock MyService. the problem is that both Mockito and Spring security use CGLIB proxies, and my service is not enhanced with #PreAuthorize after Mockito.mock(MyService.class).
Is there any way to mock service and preserve #PreAuthorize logic?
Example:
#Service
public class MyService implements IMyService {
#Override
#PreAuthorize("hasAuthority('SYSOP')")
public void someMethod(ComplexDTO dto) {
// lots of logic and dependencies, require lots of stubbing.
}
}
In order to avoid initialization of all dependencies of MyService#someMethod and building ComplexDTO at MyServiceSecurityTest I want to mock MyServiceSecurityTest but preserve #PreAuthorize checks.
You need to do integration tests and not unit tests. In general, you do not see mock classes in integration tests, at least you would not mock the class you are testing, in this I case I guess its the MyService class.
Setting up integration tests involves reading up on, but the short example below should get you on the right path
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("myProfile")
public class MyServiceIT {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private TestRestTemplate restTemplate;
#Test
public void testMyService() {
logger.info("testMyService");
//user TestRestTemplate to call your service.
}
}
EDIT: In this integration test, Spring boots up normally. That means all the annotations for security are processed and all the beans it needs to create are created and properly injected. One thing you may have to control is the Spring profile.... that can be done with the #ActiveProfiles("myProfile") annotation, which I just added to the example.

Trying to generate error about Spring's #Autowired field injection for JUnit

I am working with Spring 4.0.7, and with JUnit about testing of course.
About DI Spring offers #Autowired to be used in three locations
constructor
setter
field
I always work through the two first, why never the third option?
Because I remember have read long time ago about field injection should not be used, because it has a negative consequence about Testing. It does JUnit fails.
Note: Only for Testing is the problem. To runtime or production all goes well
Objective: For demonstration/academic purposes I want generate this problem.
I have the following:
Repository
public interface PersonRepository extends JpaRepository<Person, String>{
}
A Service
#Service
#Transactional
#Profile("failure")
public class PersonFailTestServiceImpl implements PersonService {
private static final Logger logger = ...
#Autowired
private PersonRepository personRepository;
Other Service (calling or using the service shown above)
#Service
#Transactional
#Profile("failure")
public class PersonFailTestProcessImpl implements PersonProcess {
private static final Logger logger = ...
#Autowired
private PersonService personService;
How you can see the two services are based on Field Injection.
Now the testing:
How the beans are loaded
#Configuration
#ComponentScan( basePackages={"com.manuel.jordan.server.infrastructure"},
basePackageClasses={PersonProcess.class,PersonRepository.class, PersonService.class})
public class CentralConfigurationEntryPoint {
}
#ContextConfiguration(classes=CentralConfigurationEntryPoint.class)
public class CentralTestConfigurationEntryPoint {
}
Now the two testing classes
#Transactional
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles({"development","failure"})
public class PersonServiceImplDevelopmentFailureTest extends CentralTestConfigurationEntryPoint {
#Autowired
private PersonService personService;
#Test
public void savePerson01(){
Person person01 = PersonFactory.createPerson01();
personService.save(person01);
personService.printPerson(personService.findOne("1"));
}
#Transactional
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles({"development","failure"})
public class PersonProcessImplDevelopmentFailureTest extends CentralTestConfigurationEntryPoint{
#Autowired
private PersonProcess personProcess;
Well all the testing methods pass, all green. I don't know if I am missing something or through Spring 4 the problem has been fixed
If this was your premise or problem
Because I remember have read long time ago about field injection
should not be used, because it has a negative consequence about
Testing. It does JUnit fails.
then you thought wrong. There is nothing inherently wrong with using field injection, definitely nothing that would cause JUnit tests to fail in and of itself. If a bean exists, Spring will be able to inject it whether it's in a constructor, a setter method, or a field.
Since you've activated your failure profile, your PersonFailTestServiceImpl bean will be found.
I think I can help. The example code you've posted here is a good example of a system / integration test, not a UNIT test.
If you were UNIT testing PersonFailTestProcessImpl, you would have to set the personRepository dependency yourself through code. But it is private, so how do you do this? You cannot use a constructor or setter since none is provided. This is what is meant by 'hard to unit test'.
Java 5+ provides a way to set private variables like this via reflection (the so-called privileged accessor). Basically, you obtain the class, get the declared field, call its setAccessible method, then you can set its value directly. There are libraries that will do these steps for you, but the point is that this is a pain compared to X.setSomething();
So there is nothing that 'makes jUnit fails' by using #Autowired on a private field. But building an object model without constructors or setters for establishing dependencies is unnecessarily constraining.

spring aspect being applied outside of app context (in stubbed out unit test)

Here's a weird one. I've got a few tests failing because an aspect is being applied, so an autowired service is null, bad things ensue. The issue is that I can't understand how the aspect is even being applied, since in the test I construct the object under test with new.
#RunWith(MockitoJUnitRunner.class)
public class TheControllerTest {
#Spy
private TheController controller = new TheController();
#Mock
private HttpServletRequest request;
#Mock
private ConfigService configService;
....
#Before
public void setup() {
controller.setConfigService(configService);
....
}
#Test
public void testGetAccountsList() throws Exception {
Mockito.when(accountService.getAllAccounts()).thenReturn(Arrays.asList(account1, account2));
Map<String, Object> result = controller.getAccountsList(request);
...
}
}
I'm obviously omitting plenty of code, but really, I just don't understand how, given how controller is instantiated, it could have had the advice applied.
One possible reason could be if you are running this in Eclipse - in a project with ajbuilder enabled, even if you are explicitly expecting Spring AOP through dynamic proxies, ajbuilder would actually perform compile team weaving, and hence you would see advice enhanced classes even using normal "new". Can you please check this, the fix would be to disable "ajbuilder" - here is one reference - JUnit weaving wrong Spring AOP Bean

Resources