Kotlin, Spring book, Mockito, #InjectMocks, Using different mocks than the ones created - spring-boot

I am trying to test a class like
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest(classes = [TestConfig::class])
#ExtendWith(MockitoExtension::class)
class TotalCalculatorTests {
#Mock
lateinit var jdbcRepo: JDBCRepo
#Mock
lateinit var userCache : LoadingCache<ApproverLevel, List<Approver>>
#InjectMocks
lateinit var calculator: TotalCalculator
#Test
fun totalOrder() {
// val calculator = TotalCalculator(userCache,jdbcRepo)
calculator.total(ItemA(),ItemB())
verify(jdbcRepo).getTotal()
}
}
I get an error Actually, there were zero interactions with this mock. but if I uncomment the // val calculator = TotalCalculator(userCache,jdbcRepo) it works. I assumed mockito would be using the mocks from the test class but that appears to not be true. How can I get the instance of JDBCRepo being used by the #InjectMocks?

It looks like you would like to run the full-fledged spring boot test with all the beans but in the application context you would like to "mock" some real beans and provide your own (mock-y) implementation.
If so, the usage of #Mock is wrong here. #Mock has nothing to do with spring, its a purely mockito's thing. It indeed can create a mock for you but it won't "substitute" the real implemenation with this mock implementation in the spring boot's application context.
For that purpose use #MockBean annotation instead. This is something from the spring "universe" that indeed creates a mockito driven mock under the hood, but substitutes the regular bean in the application context (or even just adds this mock implementation to the application context if the real bean doesn't even exist).
Another thing to consider is how do you get the TotalCalculator bean (although you don't directly ask this in the question).
The TotalCalculator by itself is probably a spring been that spring boot creates for you, so if you want to run a "full fledged" test you should take the instance of this bean from the application context, rather than creating an instance by yourself. Use annotation #Autowired for that purpose:
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest(classes = [TestConfig::class])
#ExtendWith(MockitoExtension::class) // you don't need this
class TotalCalculatorTests {
#MockBean
lateinit var jdbcRepo: JDBCRepo
#MockBean
lateinit var userCache : LoadingCache<ApproverLevel, List<Approver>>
#Autowired // injected by the spring test infrastructure
lateinit var calculator: TotalCalculator
#Test
fun totalOrder() {
// val calculator = TotalCalculator(userCache,jdbcRepo)
calculator.total(ItemA(),ItemB())
verify(jdbcRepo).getTotal()
}
}
Now, all this is correct if your purpose to indeed running the full microservice of spring boot and make an "integration" testing
If alternatively you just want to check the code of your calculator, this might be an overkill, you can end up using mockito and plain old unit testing. In this case however you don't need to even start the spring (read create an application context) during the test, and of course there is no need to use #SpringBootTest / #MockBean/#Autowired.
So it pretty much depends on what kind of test is really required here

Related

MeterRegistry counter test case failing

I have implemented Micrometer Prometheus counter in my service by injecting MeterRegistry and incrementing the count as shown below, and I have written a test case as well, but when I am running the test case, I am getting:
"java.lang.NullPointerException: Cannot invoke
"io.micrometer.core.instrument.MeterRegistry.counter(String,
String[])" because "this.meterRegistry" is null".
Service file:
#Autowired
private MeterRegistry meterRegistry;
public void counterIncrement() {
meterRegistry.counter("test_count").increment();
}
Test case file:
#MockBean
private MeterRegistry registry;
#Test
void testCounter() {
// invoking counterIncrement();
}
How do you create your class under test?
Since the registry is never instantiated, something seems up with how you setup your test.
Check that you are using the #MockBean in the correct way. This will replace the bean in the application context and if you do not spin up a spring context in your test, it will not work. See this post for more info.
A different approach would be to use #Mock and inject the registry in the constructor, example:
#ExtendWith(MockitoExtension.class)
public class MyServiceTest {
#Mock
private MeterRegistry registry;
private MyService myService;
#BeforeEach
void setup() {
myService = new MyService(registry);
}
#Test
void testCounter() {
var counter = mock(Counter.class);
given(registry.counter(any(String.class))).willReturn(counter);
myService.counterIncrement();
}
You can test metrics without Mockito using SimpleMeterRegistry
#Test
void testCounter() {
var meterRegistry = new SimpleMeterRegistry();
Metrics.addRegistry(meterRegistry);
// invoke counterIncrement();
var actual = meterRegistry.counter("test_count").count();
assertEquals(1.0d, actual);
}
Depending on which junit version you are using you need to add the annotation to your test class. Junit 5: #ExtendWith(MockitoExtension.class) or for Junit 4: #RunWith(MockitoJUnitRunner.class)
Depending on the test and the service there are several ways to deal with the missing MeterRegistry.
If you use a spring context in your test, try to use a test configuration to create the MeterRegistry bean.
If your test uses some Mock framework, you could mock the MeterRegistry as suggested by by #Hans-Christian.
If you simply make the member meterRegistry non-private. You could set it to a SimpleMeterRegistry in some setup method, anotated with #BeforeEach as suggested by #checketts in the comments.
If mocking the meter registry gets complicated, you could easily build and use some factory that provides the registry and mock this factory. A very easy factory will do, e.g. a spring #Component with an autowired MeterRegistry and some public getter for the factory.
You could use the factory method pattern as described in wikipedia to get the MeterRegistry, overwrite the factory method in a subclass of your service and use this subclass in the test. (Note that the gang of four did use a static factory method, you'll need a non-static method.)
I favour solution 3 but would use solution 1 whenever appropriate. I've added solutions 4 and 5 just because there might be some additional reasons and special cases that make these solutions a good choice. If so, I prefer 4 over 5.

#WebMvcTest vs #ContextConfiguration when testing WebSocket controllers

I'm currently in the process of writing tests for my controllers in a Spring Boot project that uses WebSockets.
As information on the subject is hard to come by, my only lead is this example recommended by the docs.
Allow me to attempt to explain my forays so far, trying to understand and get my test environment set up. I'm following the context-based approach, and I'm torn between #WebMvcTest and #ContextConfiguration (which the example uses).
My motivation behind using #WebMvcTest at all was this line from the Spring Boot docs:
When testing Spring Boot applications, this [using #ContextConfiguration(classes=…​) in order to specify which Spring #Configuration to load, or using nested #Configuration classes within your test] is often not required. Spring Boot’s #*Test annotations search for your primary configuration automatically whenever you do not explicitly define one.
#WebMvcTest thus seems particularly suited to the task as it focuses only on the web layer by limiting the set of scanned beans to only those necessary (e.g. #controller) rather than spinning up a complete ApplicationContext.
The code example I'm following uses field injection to initialize channel interceptors to capture messages sent through them.
#Autowired private AbstractSubscribableChannel clientInboundChannel;
#Autowired private AbstractSubscribableChannel clientOutboundChannel;
#Autowired private AbstractSubscribableChannel brokerChannel;
As far as I can tell, these fields are what makes the presence of the TestConfig class (see code block at the end for full class definition) in the example necessary, since without it, I get an error saying no beans qualify as autowire candidates. I believe these two fields in TestConfig are the key:
#Autowired
private List<SubscribableChannel> channels;
#Autowired
private List<MessageHandler> handlers;
However, without #ContextConfiguration(classes = [WebSocketConfig::class]) (WebSocketConfig being my own WebSocket configuration file), these two fields are always null resulting in errors.
So far, this would mean that #ContextConfiguration(classes = [WebSocketConfig::class]) in combination with the presence of TestConfig are needed.
The interesting thing is that without #WebMvcTest, clientInboundChannel, clientOutboundChannel, and brokerChannel are never actually initialized. So what this leaves me with is that I need both #WebMvcTest and #ContextConfiguration, which somehow seems odd.
And with the last update to the example repo being more than two years old, I can't shake the feeling that it may be somewhat outdated.
This is what my test class (Kotlin) currently looks like. I've omitted the createRoom test case for brevity:
#WebMvcTest(controllers = [RoomController::class])
#ContextConfiguration(classes = [WebSocketConfig::class, RoomControllerTests.TestConfig::class])
class RoomControllerTests {
#Autowired
private lateinit var clientInboundChannel: AbstractSubscribableChannel
#Autowired
private lateinit var clientOutboundChannel: AbstractSubscribableChannel
#Autowired
private lateinit var brokerChannel: AbstractSubscribableChannel
private lateinit var clientOutboundChannelInterceptor: TestChannelInterceptor
private lateinit var brokerChannelInterceptor: TestChannelInterceptor
private lateinit var sessionId: String
#BeforeEach
fun setUp() {
brokerChannelInterceptor = TestChannelInterceptor()
clientOutboundChannelInterceptor = TestChannelInterceptor()
brokerChannel.addInterceptor(brokerChannelInterceptor)
clientOutboundChannel.addInterceptor(clientOutboundChannelInterceptor)
}
#Test
fun createRoom() {
// test room creation
// ...
}
#Configuration
internal class TestConfig : ApplicationListener<ContextRefreshedEvent?> {
#Autowired
private val channels: List<SubscribableChannel>? = null
#Autowired
private val handlers: List<MessageHandler>? = null
override fun onApplicationEvent(event: ContextRefreshedEvent) {
for (handler in handlers!!) {
if (handler is SimpAnnotationMethodMessageHandler) {
continue
}
for (channel in channels!!) {
channel.unsubscribe(handler)
}
}
}
}
}
The answer posted here was my first clue towards starting to understand how #WebMvcTest and #ContextConfiguration are meant to be used.
It would appear the line that is really necessary is #ContextConfiguration(classes = [WebSocketConfig::class]), since without it, the necessary configuration (my own WebSocketConfig, in this case) can't be found.
While #WebMvcTest (and test slices in general) does automatically search for the primary configuration, the configuration defined by the main application class is the one that's found since that class is the one annotated with #SpringBootApplication.
What's more, test slices such as #WebMvcTest flat out exclude #configuration classes from scanning, meaning they won't be included in the application context loaded by the test slice.
Supporting this is the fact that the test works if #WebMvcTest is replaced with #SpringBootTest — even without #ContextConfiguration(classes = [WebSocketConfig::class]) — meaning that WebSocketConfig was loaded.
To summarize, as I see it, there are 3 ways to include a non-primary configuration in the application context so that it's detected in integration tests:
Annotate the test class with #SpringBootTest (and optionally #ContextConfiguration to restrict the number of loaded beans to only those needed)
Annotate the test class with a test slice (e.g. #WebMvcTest) in conjunction with #ContextConfiguration
Annotate the test class with a test slice (e.g. #WebMvcTest) and define the configuration in the main class (i.e. the one annotated with #SpringBootApplication)

How to achive #Capturing like behavior in MockK?

We have Spring application unit tested using JMockit mock framework. Now we would like to write new tests in Kotlin using MockK. Almost everything seems to work fine but we can't figure out how to mock beans autowired by Spring. With JMockit we used to use #Capturing annotation that extended the mocking also on classes implementing the mocked interface. How can I achive similar behavior of a mock in the MockK framework?
Bringing oleksiyp comments to an answer
Currently, Mockk doesn't have that kind of behavior. Its support to Spring is limited, but there's a workaround using Spring itself:
You can create a bean however you want, even in Integration Tests. When creating a bean, you can instantiate a mock:
#Bean
fun bean(): BeanType = mockk()
Then, when this bean is autowired, it will return the mocked instance, and you'll be able to set it's behavior using Mockk's DSL as usual.
Spring documentation recommends that all your components be autowired through the constructor. If you follow that convention you wouldn't have this problem.
To be specific, the recommendation is as following...
#RestController
class SomeRandomController(
#Autowired private val ARepository: aRepository,
#Autowired private val BRepository: bRepository,
#Autowired private val CRepository: cRepository
){ etc ...}
Then in your test you will need the following lines:
val aRepository = mockk<ARepository>(relaxed = true)
val bRepository = mockk<BRepository>(relaxed = true)
val cRepository = mockk<CRepository>(relaxed = true)
val controller = SomeRandomController(aRepository, bRepository, cRepository)

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.

How to inject Mocks into Spring Service

Environment :
Spring MVC 4
Junit
Mockito
Code :
Spring Service under test :
#Service("abhishekService")
public class AbhishekServiceImpl implements AbhisheskService {
#Autowired
private DaoOne daoOne;
#Autowired
private DaoTwo daoTwo;
#Autowired
private DaoThree daoThree;
#Autowired
private DaoFour daoThree;
}
Junit Test :
public class AbhishekServiceImplTest {
#Mock
private DaoOne daoOne;
#Mock
private DaoTwo daoTwo;
#Mock
private DaoThree daoThree;
#Mock
private UserDao userDao;
private AbhisheskService abhisheskService;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
abhisheskService = new AbhishekServiceImpl();
}
}
Issue :
1)As shown in code snippet one , the class under test uses four dependencies.
2)As shown in code snippet two , in junit test case class , all 4 dependencies are mocked using #Mock
3)My question is : how these four mocked objects should be injected into test class ?
4)My class under test doesn't have constructor/setter injection but field injection using #Autowired.
5)I don't want to use #InjectMocks annotation due to its dangerous behavior
as mentioned here
Can anybody please guide on this ?
You are trying to test a class wrongly designed to test the behavior i.e. the properties are not accessible to be mocked. AbhishekServiceImpl has to provide a way to inject the mocks to the class. If you cannot access the fields then it is a clear case of wrongly designed class. Considering that the AbhishekServiceImpl is a class in a legacy code and you are trying to test the behaviour then you can use reflection to inject the mock objects as below:
DaoOne mockedDaoOne = mock(DaoOne.class);
when(mockedDaoOne.doSomething()).thenReturn("Mocked behaviour");
AbhishekService abhishekService = new AbhishekServiceImpl();
Field privateField = PrivateObject.class.getDeclaredField("daoOne");
privateField.setAccessible(true);
privateField.set(abhishekService, mockedDaoOne);
assertEquals("Mocked behaviour", abhishekService.doSomething());
Its very rare that you test behaviour of a class that you have not written yourself. Though I can imagine a use case where you have to test an external library because its author did not test it.
You can mark the junit test with #RunWith(SpringJUnit4ClassRunner.class) and then use #ContextConfiguration to define a context which instantiates the DAOs and service and wires them together.

Resources