Is it possible to use multiple layers of inherited #MockBean? - java

I am trying to organize our usage of #MockBean to reduce the number of ApplicationContexts in use during our testing. My approach is to put shared
mocks in super classes and then inherit into the actual test code.
I have a hierarchy like this:
BaseTest:
#ActiveProfiles({"test", "human-readable-logging"})
#ContextConfiguration(classes = Main.class)
#ExtendWith(SpringExtension.class)
#SpringBootTest(properties = {"spring.cloud.gcp.credentials.location="})
#AutoConfigureMockMvc(print = MockMvcPrint.NONE)
public class BaseTest {}
BaseUnitTestMocks: this has two subclasses
public class BaseUnitTestMocks extends BaseTest {
#MockBean private ServiceA serviceA;
#MockBean private ServiceB serviceB;
... accessors ...
BaseUnitTest: this is on the inheritance line with the problem.
#Tag("unit")
#ActiveProfiles("unit-test")
public class BaseUnitTest extends BaseUnitTestMocks {}
BaseUnitTestPlusOneMock: this mock seems to not make it into the application context
public class BaseUnitTestPlusOneMock extends BaseUnitTest {
#MockBean private ServiceC serviceC;
... accessor ...
}
The test class looks like this:
public class MyTest extends BaseUnitTestPlusOneMock {
private static final FAKE_THING = "fake_thing";
#BeforeEach
public void setup() throws IOException {
when(mockServiceC().getRequiredThing(any())).thenReturn(FAKE_THING);
}
... tests ...
Expected Result:
The mock is included in the application context and used in the Autowired in ServiceC.
Actual Result:
This works when run from the debugger in IntelliJ. When run in a clean environment (cleaned project and stopped all gradle daemons), not in the debugger it fails because the actual ServiceC is used.

Related

Running Spring JUnit tests without #InjectMocks

I am trying to test a class (and it’s methods) using a JUnit test without the #InjectMocks annotation. The annotation will not work for me because the class I am attempting to test has class attributes that #InjectMocks will not create.
Here is what my functional class looks like:
(File path: src/main/java/com/mycompany/mypackage)
package com.mycompany.mypackage;
#Service
public class MyClass {
#Value(“${someString}”)
private String someString;
public printString() {
System.out.println(someString);
}
}
Here is what my testing class looks like:
(File path: src/test/java/com/mycompany/mypackage)
package com.mycompany.mypackage;
#RunWith(SpringJUnit4ClassRunner.class)
public class MyClassTest {
#InjectMocks
MyClass myClass
#Test
public testPrintString() {
myClass.printString()
}
}
I’ve tried to use #Autowired in the place of #InjectMocks to fully instantiate the MyClass bean, but this will not actually create a instance of MyClass, and I will get a null pointer exception whenever I try to use the ‘myClass’ variable in a test.
For my particular use case, using a constructor method is not a realistic alternative.
Any help would be awesome!
Try adding this to your test class to set the value using reflection:
import org.junit.Before;
import org.springframework.test.util.ReflectionTestUtils;
#Before
public void setup()
{
ReflectionTestUtils.setField(
myClass,"someString",
"SOME_VALUE", String.class);
}
If you do not want mock test, you should do as below.
// #RunWith(SpringJUnit4ClassRunner.class)
#RunWith(SpringRunner.class)
#SpringBootTest(classes = YourApplication.class) // YourApplication is your spring boot app main class used to import spring context
public class MyClassTest {
// #InjectMocks
#Autowired
MyClass myClass;
#Test
public void testPrintString() {
myClass.printString();
}
}

How to do a BeforeEach on all of my integration test files

I have a bit of code I'd like to run in every #BeforeEach of all my integration tests files. Basically the code I need to add is the following :
#MockBean
RequestInterceptor interceptor; // I NEED THIS
#BeforeEach
public void initTest() throws Exception {
Mockito.when(interceptor.preHandle(any(), any(), any())).thenReturn(true); // AND THIS
}
Is there a way to avoid duplicating this part in every files ? Maybe I can create a test configuration file and use an annotation in my test files. As I am very new to java spring boot I would appreciate some help. Thanks.
You can create super class e.g. BaseTest and move this code there. And then every your test should just extend BaseTest. Even more you can set all Annotation in this class. E.g.:
#AutoConfigureMockMvc
#MockitoSettings(strictness = Strictness.STRICT_STUBS)
#ExtendWith(MockitoExtension.class)
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
#SpringBootTest
public class BaseTest {
#MockBean
RequestInterceptor interceptor; // I NEED THIS
#BeforeEach
public void initTest() throws Exception {
Mockito.when(interceptor.preHandle(any(), any(), any())).thenReturn(true); // AND THIS
}
}
And then all your tests:
class MeasurementsServiceTest extends BaseTest {
//test methods here
}
Well you can make a parent base class that would have the BeforeEach method and then just inherit that class on all of the other ones
public class BaseTestClass {
#BeforeEach
public void setUp(){
System.out.println("Base Test Class");
}
}
public class InheritsFromBase extends BaseTestClass {
// here goes test code
}

Spring not calling #Bean method in tests

I have a Repository MyRepository which is a #Repository. This repository is used by one of my rest controllers. What I want to test is if authorization of my rest controller works properly, thus my tests use #WithUserDetails. I want to mock a call to MyRepository by following this tutorial. When I run my tests I get an exception saying:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
Through some debugging I found out that my MockConfig#myRepository method is not being called.
src/main/java/com.example
MyRepository
#Repository
interface MyRepository extends CrudRepository<MyEntity, Long> {}
src/test/java/com.example
MockConfig
#Profile("test")
#Configuration
public class MockConfig
{
#Bean
#Primary
public MyRepository myRepository()
{
return Mockito.mock(MyRepository.class);
}
}
MyTestClass
#ActiveProfiles("test")
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class
})
class MyTestClass
{
#Autowired
private MockMvc mvc;
#Autowired
private MyRepository myRepository;
#Test
#WithUserDetails("some_user")
public void testWithCorrectPermissions()
{
long entityId = 1;
MyEntity mockReturnValue = new MyEntity();
Mockito.when(myRepository.findOne(entityId)).thenReturn(mockReturnValue);
Mockito.when(myRepository.save(mockReturnValue)).thenReturn(mockReturnValue);
this.mockMvc.perform(post("my/api/path")).andExpect(status().isOk());
}
}
Try with the solution proposed in How to exclude a #Repository from component scan when using Spring Data Rest
Add the following annotation to your test class
#EnableJpaRepositories(excludeFilters = {#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {MyRepository.class})})
class MyTestClass
(...)
If you want to mock the dependency(eg repository) for your testing class, I would suggest you to use MockitoJUnitRunner.class as SpringRunner.class will initialise the Spring application content, which will cause the testing to be slower and also more dependencies required depending on your project configuration.
So, for your MyTestClass
#RunWith(MockitoJUnitRunner.class)
public class MyTestClass{
#Mock
private MyRepository myRepository;
private MyTest myTest;
#Before
public void setUp() throws Exception {
myTest = new MyTest(myRepository);
}
#Test
public void test(){
...
when(myRepository.get(anyInt()).thenReturn(new MyEntity());
...
}
There is some reference here.
If you insist to test using the current implementation, it might be that the MyRepository was scanned by the Spring Data and the bean was initialised by it. You might want to disable the component scanning as recommended by user2456718.

Is it good to exercise class which is injected in the parent?

Let's assume I have a MyServiceTest class which is extended by some HelperTest class which has context configuration defined and injected service like this:
#ContextConfiguration(locations = { "classpath:META-INF/context.xml" })
public abstract class HelperTest {
#Autowired
private MyService myService;
}
This helper class helps test classes to prepare the system for testing.
Now I want to test MyService class which means that I need to inject this service in my MyServiceTest class:
#RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest extends HelperTest {
#Autowired
private MyService myService;
}
But in general because MyServiceTest is inherited I could just define MyService in HelperTest as protected and use it in MyService class.
Is it right thing to do or there are some other things which might influence when I use bean injection via spring in this way?

Create instance of call with autowire

I have to create a instance of a class, that have autowired elements, for test.
public class MyClass extends SomeOtherClass {
#Autowired
public MyClass(OtherClass1 one, OtherClass2 two){
super(one, two)
}
}
How can i in code create instance of this class, with the arguments wired in though spring?
Your test can be made Spring-aware if you use the SpringJUnit4ClassRunner to read in your Spring Context to be used in the test. For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"the-config.xml"})
public final class MyClassTests {
#Autowired
private MyClass testee;
#Test
public void testSomething() {
assertThat(testee).doesSomethingExpected();
}
}
Note that you should reuse as much of your production config as possible and not create a parallel Spring Context config that mirrors it.
Instead of passing the other elements in as constructor arguments, you Autowire them as properties. Spring will then inject the objects.
public class MyClass extends SomeOtherClass {
#Autowired
private OtherClass1 one;
#Autowired
private OtherClass2 two
public MyClass(){
super(one, two)
}
}
Edit: Based on http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/, adding #Autowired to the constructor is also valid.
If you want to Autowire MyClass, you must annotate it with #Component or a similar annotation such as #Service.
#Component
public class MyClass extends SomeOtherClass
Then, you can use it in other classes
public class ClassThatUsesMyClass {
#Autowire
private MyClass myClass;
}

Categories

Resources