Can I Write Aspect for my Junit Test Cases in SpringBoot - java

I have a branch of Test cases that are using the same code repeatedly for different test scenarios/methods. It's like infra related maintenance code. Suppose need to validate some business logic before we need to perform some infra-related operation. Same after the execution of business logic like cleaning all the infra-related configs. Thought we can use Aspects. As our main services are written in spring boot.
#TestInstance(Lifecycle.PER_CLASS)
#TestMethodOrder(OrderAnnotation.class)
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class RegressionTest{
#Test
#Order(1)
public void Test1(){
}
#Test
#Order(2)
public void Test2(){
}
#Test
#Order(3)
public void Test3(){
}
#Test
#Order(4)
public void Test4(){
}
#Test
#Order(5)
public void Test5(){
}
#Test
#Order(6)
public void Test6(){
}
}
In the above example suppose want to run some common code before and after some test cases with the different values( 1 , 5 use some maintenance code with a different activity, and 2,6 use some different ). Depends on the test case. I planning to use Aspect so that no need to write same code repeatedly. Please suggest if you have a better suggestion or how can I use aspect-oriented programming in this scenario.

You can only apply Spring AOP to Spring beans/components. I.e., your test would need to e.g. have a #Component annotation in order to be picked up by a correspondingly configured component scan or be instantiated in a #Bean factory method. I am not a Spring user, i.e. I do not have much experience in using Spring from tests either. But what I do know is that you could simply use native AspectJ, either applied by compile-time or load-time weaving, in order to achieve what you want. That would be pretty straightforward to implement. I can certainly help you, if you present an MCVE on GitHub, so I do not have to start from scratch.
If you want to use a non-AOP solution, why not simply use the existing #BeforeAll and/or #BeforeEach annotations, just like #asafb suggested before? I see no reason why that would not work.

Related

I want to test a controller without using #SpringBootTest

I have the following test class:
#SpringBootTest
public class ChoreControllerTest
{
#Autowired
private ChoreController controller;
#Test
public void throwOnMissingChore()
{
assertThrows(ChoreNotFoundException.class, () -> this.controller.getChore(0L));
}
}
It takes about 5 seconds for Spring Boot to start up so the test can run. I want to reduce this time, but if I just remove the #SpringBootTest annotaton, I just get a NPE.
Is there a way to make this controller test more lightweight, or am I stuck with the startup time? I'm especially worried about what will happen to my test times if I ever want to test more than one controller....
The #SpringBootTest annotations create a Spring Context for you therefore it takes a while to start up. This annotation is mostly used for integration tests where a Spring context is required. Here are a few tips for optimizing integration tests.
If you remove the annotation the ChoreController cannot be autowired (no context available) which results in a NullpointerException.
Depending on your needs you can just use a Mocking library like Mockito to inject mocks e.g. services that your controller class needs and run the test without the #SpringBootTest.
You might want to take a look at this article for setting up those mocks properly.

Dagger2: How to use #Inject in a JUnit test?

I would like to have the ability to inject dependencies into JUnit tests with Dagger 2 (I'm new to this framework). Coming from Spring, there you can do something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyTestClass {
#Autowired
private MyService service;
#Test
public void testMySerivce() { /* ... */ }
}
... but with Dagger 2 I have not yet found a solution that does not rely on an explicit DaggerMyComponent.builder().build().myService().
Ideally, I would imagine the solution to look something like the following:
// tell JUnit that dagger needs to do some post processing
#RunWith(DaggerJUnit4Runner.class)
// tell dagger which component classes to use for injection
#Components(MyComponent.class)
public class MyTestClass {
#Inject
private MyService service;
#Test
public void testMySerivce() { /* ... */ }
}
Unfortunately, there is no DaggerJunit4Runner.
Any hints on how this could be accomplished would be greatly appreciated.
I haven't seen any built-in feature for this, or any prominent testing library that supports this.
Dagger does all its dependency wiring at compile time, and only in ways you tell it to; unlike Spring, there's no code written to read a testing class at runtime or supply the dependencies it needs. Dagger's appeal over Guice and Spring comes from compile-time compilation, validation, and optimization. Though what you describe would be very useful, that kind of reflection is antithetical to Dagger's original motivations. Either your Dagger component is compiled with the generated code to inject the test, or you need to be able to pull those dependencies out independently as you've listed above.
For unit tests you'll probably need to skip Dagger and create your classes or their mocks manually; for system or integration tests, you'll need to expose all of the classes you need on the component definition. If you want to replace dependencies with test doubles for repeatability or isolation, you'll need to make your component sufficiently configurable to accept the replacement implementations, or you'll need to create a new for-testing Component implementation that uses the test doubles instead of the real implementations.

Is Spring Framework used in a unit test?

I am getting ready to take my Spring Certification v5.0 and there appears to be a question: Do you use Spring in a unit test? Link to Exam Guide questions.
From Spring reference guide I know this:
The POJOs that make up your application should be testable in JUnit or
TestNG tests, with objects simply instantiated using the new operator,
without Spring or any other container.
From my study as well I can tell that we only are using Spring when testing controllers (like below), repositories or when creating integration tests and maybe some other cases. We would need the TestContext in these cases which is part of org.springframework.* package.
#RunWith(SpringRunner.class)
#WebMvcTest(HelloWorldController.class)
So, is the correct answer of this question: No we do not use Spring? or that, Yes we do need it. Because we obviously use it in some cases.
The first paragraph you mentioned is the answer to your question, you don't need Spring to write unit tests for the classes you wrote, even when they're Spring beans.
The other cases you mentioned aren't really unit tests. When testing a repository using SpringRunner and a mock database, you're no longer writing a unit test, but an integration test. The same applies to writing tests for your controller using MockMvc. In both cases you're testing the integration between the Spring framework (Spring MVC or Spring Data) with your code (and a database).
However, you can write unit tests for your controller, but in that case, you would rather do something like this:
#Controller
public class HelloWorldController {
#RequestMapping("/hello")
public ModelAndView getHello() {
return new ModelAndView("hello", "title", "hello world");
}
}
public class HelloWorldControllerTest {
private HelloWorldController controller;
#Before
public void setUp() {
controller = new HelloWorldController();
}
#Test
public void getHelloShouldUseHelloView() {
assertThat(controller.getHello().getViewName()).isEqualTo("hello");
}
#Test
public void getHelloShouldAddATitleModel() {
assertThat(controller.getHello().getModel()).containsEntry("title", "Hello world");
}
}
Spring is a framework. A framework calls you, so by definition, using spring with your controller is an integration point and does not belong in a unit test.
I have successfully been testing my controllers' behaviour without involving spring whatsoever.
I have also tested the controller's annotation successfully by asking spring to mock the controller and invoking the mockMvc.

Autowired implementation based on profile Spring Boot

I am developing a REST API with Spring Boot.The problem it's that I have one interface and two implementations and I want to test only with the mock implementation.
Interface CRMService
#Service
CRMServiceImpl
#Service
CRMServiceMock
Implementations: the first one is the real integration with the backend and the second is a mock for testing purposes, what's the best approach? Integration test or test based on the active profile ? If I need to autowire a service based on profile what's the best practice?
While I'm sure there's exceptions, generally it shouldn't be integration or unit tests (often involves mocks), but both; see testing pyramid concept.
Integration tests: just use the real service. If it calls out to other live services, then consider injecting the URLs as Spring Boot properties which point to mock servers in the test environment (Node.js or something easy and quick).
Unit tests: Consider using a test-framework like Mockito. Using this you can write your tests with mocks approximately like so:
private CRMServiceImpl mockService = mock(CRMServiceImpl.class);
#Test
public void someTest() {
when(mockService.someMethod(any(String.class), eq(5))).thenReturn("Hello from mock object.")
}
The above example roughly translates to "when some class invokes 'someMethod(String, int)' on your service, return the String specified".
This way allows you to still use mocks where necessary, but avoids having to maintain entire mock implementation profiles and avoids the problem of what to auto-wire.
Finally, if you need a full separate implementation, consider not auto-wiring services! Instead, use #Bean annotations in your configuration class and inject it via constructors into the classes that need it. Something like so:
#Configuration
public class ApplicationConfiguration {
#Value{$"service.crm.inmem"} // Injected property
private boolean inMem;
#Bean
CRMService getCRMService() {
if (inMem) {
return new CRMServiceMock();
}
return new CRMServiceImpl();
}
#Bean
OtherService getOtherService() {
// Inject CRMService interface into constructor instead of auto-wiring in OtherService.class
return new OtherService(getCRMService());
}
}
An example of when you could use ^^ would be if you wanted to switch between an in-memory store, and a real database-connection layer.
Personally I'd suggest doing dependency injection like the above example even when there aren't multiple implementations since as a project grows, if an auto-wired property fails it can be difficult to track down exactly why. Additionally explicitly showing where dependencies come from can help with organizing your application and visualizing your application hierarchy.

integration testing spring service layer based on migrated data

#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
#Transactional
public class MyServiceTest {
#Resource(name="myService")
public MyService myService;
#Test
public void testSeomthing() {
//do some asserts using myService.whatever()
}
}
However the tests are based on data I migrate in, so every time I run my suite of tests I want to execute my unrelated migration code. I don't want to run a #Before in each test class. I want to run it once at beginning of complete test process, where can I put this ?
I would advice you to create a test bean somewhere with startup logic invoked in #PostConstruct:
#Service
public class TestBean {
#PostConstruct
public void init() {
//startup logic here
}
}
Obviously this bean should only be created for tests, the easiest way to achieve this is to place it in src/test/java in a package that is component-scanned by Spring for #Service-annotated classes.
Note: you must remember that #PostConstruct is not running in a transaction! See How to call method on spring proxy once initialised.
JUnit also offers a #BeforeClass annotation which you can place on a static method to initialize resources just once.

Categories