I am writting test case of my controller using mockmvc
#Mock
private AService aService;
#InjectMocks
private AController aController;
#BeforeEach
public void init() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(aController)
.setCustomArgumentResolvers(putAuthenticationPrincipal) // for passing the authentication principal
.build();
}
now when i trying to test
#Test
public void testfunction() throws Exception {
String id = UUID.randomUUID().toString();
Mockito.when(aService.getAccount(Mockito.anyString())).thenThrow(new Exception("not avalible"));
mockMvc.perform(MockMvcRequestBuilders.get("/account/{id}", id)
....
}
In this the aService.getAccount() is not getting mock. that is why I am not getting the desired result..
I didn't understand why the mocking of function isn't working in this case.
In a #WebMvcTest, you should not create a mock of your controller. Spring needs to create a controller in it’s context, and inject collaborating services into it.
To do that:
annotate the test class with #WebMvcTest(YourController.class)
annotate collaborating services with #MockBean, not #Mock
Related
I would like to run an integration test of a single bean with resilience4j annotated method in a spring boot app. My intent is to test resiliency of bean method calls while not loading the full spring context.
The setup is as follows:
Dependencies include the following:
io.github.resilience4j:resilience4j-spring-boot2
io.github.resilience4j:resilience4j-reactor
org.springframework.boot:spring-boot-starter-aop
The resilience4j time limited spring bean with method to test:
#Service
public class FooService {
#TimeLimiter(name = "fooTimeLimiter")
public FooResponse foo() {
//entertain operation that might timeout
}
}
Configuration:
resilience4j.timelimiter.instances.fooTimeLimiter.timeoutDuration=1s
And the test:
#SpringBootTest
#ContextConfiguration(classes = FooService.class)
public class FooServiceIT {
#Autowired
private FooService service;
#MockBean
private Bar bar;
#Test
void foo_timeout() {
//setup mocks so the operation delays the output and shall end up with timeout
Assertions.assertThrows(TimeoutException.class, () -> service.foo());
}
}
However, the TimeLimiterAdvice.proceed() is not entertained, no timeout exception is thrown and the test fails.
Same question has been asked here: Testing SpringBoot with annotation-style Resilience4j but there is no solution.
I tried both approaches - implement FooService interface and program directly using the concrete class. With the same result.
How can I achieve the time limiter annotation is taken into account in my test?
Edit: I even tried plain spring test (no spring boot) with the following setup:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class FooServiceIT {
#Configuration
#Import({TimeLimiterConfiguration.class, FallbackConfiguration.class, SpelResolverConfiguration.class})
static class ContextConfiguration {
#Bean
public FooService fooService() {
//prepare bean;
}
#Bean
public TimeLimiterConfigurationProperties timeLimiterConfigurationProperties() {
return new TimeLimiterConfigurationProperties();
}
}
#Autowired
private FooService service;
//tests...
}
Same result (i.e. no timeout exception).
When dealing with SpringBootTest and #CircuitBreaker, it was sufficient to add #EnableAspectJAutoProxy annotation to the test. After this change, the CircuitBreakerAspect was entertained and the test behaves as expected.
In order to make #TimeLimiter working as expected, one need to add #Bulkhead annotation to the method as well.
The updated method looks as follows:
#Bulkhead(name = "fooBulkhead", type = Type.THREADPOOL)
#CircuitBreaker(
name = "fooCircuitBreaker",
fallbackMethod = "fooFallback"
)
#TimeLimiter(
name = "fooTimeLimiter"
)
public CompletableFuture<FooResponse> foo() {
//...
}
and the test:
#SpringBootTest(classes = FooService.class)
#EnableAspectJAutoProxy
#Import(value = {CircuitBreakerAutoConfiguration.class, TimeLimiterAutoConfiguration.class, BulkheadAutoConfiguration.class})
public class FooServiceIT {
//...
}
I am attempting to use TestingContainers. I was able to get it to run but my tests are always null. I am trying to avoid mocking but rather having real data.
Repository
#Sql("classpath:data.sql")
class OrderDataRepositoryTest extends AbstractTestConfiguration {
//#Mock
#MockBean
//#Autowired
private OrderDataRepository orderRepository;
private AutoCloseable closeable;
#BeforeEach
public void init() {
closeable = MockitoAnnotations.openMocks(this);
}
#AfterEach
void closeService() throws Exception {
closeable.close();
}
#Test
void getAllUsersTest() {
List<Order> orders = orderRepository.findAll();
orders.toString();
}
}
config
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Testcontainers
public abstract class AbstractTestConfiguration {
#Container
private MySQLContainer database = new MySQLContainer("mysql:8.0");
#Test
public void test() {
assertTrue(database.isRunning());
}
}
main
#SpringBootTest
#Sql("classpath:init.sql")
#TestPropertySource("classpath:application-test.yml")
class TentingContainerApplicationTests {
}
application.properties
spring:
application:
datasource:
url: jdbc:mysql:8.0:///test?TC_INITSCRIPT=file:src/main/resources/init.sql
driver-class-name: com.mysql.jdbc.Driver
The commented out
//#Mock
#MockBean
//#Autowired
is what I tried. Of course mock works out but I want real data for the #services and #repository classes.
advice?
If you want to test your database-related code in isolation (I assume you're using Spring Data JPA) then #DataJpaTest fits perfectly.
This annotation will create a sliced Spring context for you that contains only persistence relevant beans like: DataSource, EntityManager, YourRepository. This doesn't include your service classes, your #Component classes, or #RestController.
By default, this annotation tries to configure an embedded in-memory database as the DataSource. We can override this (and you already did with some of your code examples) behavior to use Testcontainers:
#DataJpaTest
#Testcontainers
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class OrderDataRepositoryTest {
#Container
static MySQLContainer database = new MySQLContainer("mysql:8.0");
#DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry propertyRegistry) {
propertyRegistry.add("spring.datasource.url", database::getJdbcUrl);
propertyRegistry.add("spring.datasource.password", database::getPassword);
propertyRegistry.add("spring.datasource.username", database::getUsername);
}
#Autowired
private OrderDataRepository orderRepository;
#Test
void shouldReturnOrders() {
}
}
If you want to write another test that includes all your beans and also starts the embedded servlet container, take a look at #SpringBootTest for writing integration tests.
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#Testcontainers
class MyIntegrationTest {
#Container
static MySQLContainer database = new MySQLContainer("mysql:8.0");
#DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry propertyRegistry) {
propertyRegistry.add("spring.datasource.url", database::getJdbcUrl);
propertyRegistry.add("spring.datasource.password", database::getPassword);
propertyRegistry.add("spring.datasource.username", database::getUsername);
}
#Autowired
private ServiceA serviceA;
#Autowired
private OrderDataRepository orderDataRepository;
}
When working with a Spring TestContext for your test and Mockito, make sure to understand the difference between #Mock and #MockBean.
So I have a couple Tests that run using mockito. One requires a static to be mocked so I use PowerMockito like this...
BDDMockito.given(WebClient.create(any())).willReturn(webClientMock);
Next I have another test that mocks a final class like this...
HttpSecurity security = Mockito.mock(HttpSecurity.class);
The second one doesn't work at first, I get the following failure....
Cannot mock/spy class org.springframework.security.config.annotation.web.builders.HttpSecurity
Mockito cannot mock/spy because :
- final class
So I looked and found this question which suggests adding the file .../src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker with mock-maker-inline
But when I add that the first line fails with...
org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class java.lang.Class
How do I get both to work?
Update
I didn't want to clutter up the question with the entire test which really doesn't provide any extra context. However, since people are insisting and seem to misunderstand the question...
// Static
#ExtendWith(MockitoExtension.class)
#RunWith(PowerMockRunner.class)
#PrepareForTest(WebClient.class)
public class RasaServiceTest {
#Mock
private WebClient webClientMock;
#Mock
private WebClient.RequestHeadersSpec requestHeadersMock;
#Mock
private WebClient.RequestHeadersUriSpec requestHeadersUriMock;
#Mock
private WebClient.RequestBodyUriSpec requestBodyUriMock;
#Mock
private WebClient.ResponseSpec responseMock;
private MyService service = new MyService();
#BeforeAll
public void setup(){
PowerMockito.mockStatic(WebClient.class);
BDDMockito.given(WebClient.create(any())).willReturn(webClientMock);
}
#Test
public void HappyPath(){
MessageResponseItem item = new MessageResponseItem();
item.setRecipientId("id");
item.setResponseText("response");
MessageResponseItem[] items = {item};
when(webClientMock.post()).thenReturn(requestBodyUriMock);
when(requestHeadersUriMock.uri("/webhooks/rest/webhook")).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(MessageResponseItem[].class)).thenReturn(Mono.just(items));
InferenceEngineRequest request = new InferenceEngineRequest();
MessageResponseItem[] result = service.call(request).block();
assertThat(result.length).isEqualTo(1);
assertThat(result[0].getResponseText()).isEqualTo("response");
}
}
// Final
#RunWith(MockitoJUnitRunner.class)
public class RemoveMeConfigTest {
#Test
public void happyPath() throws Exception {
HttpSecurity security = Mockito.mock(HttpSecurity.class);
CsrfConfigurer<HttpSecurity> csrf = Mockito.mock(CsrfConfigurer.class);
RemoveMeConfig config = new RemoveMeConfig();
Mockito.when(
security.csrf()
).thenReturn(csrf);
Mockito.when(
csrf.disable()
).thenReturn(security);
config.configure(security);
Mockito.verify(
security,
Mockito.times(
1
)
).csrf();
Mockito.verify(
csrf,
Mockito.times(1)
).disable();
}
}
Unit test noob here.
I have three classes: Db1Dao, Db2Dao, ExecuteClass where Db1Dao, Db2Dao are database access objects for two different databases. My goal is to fetch some data from db1 using Db1Dao and run executeClass.execute() to "put" the processed data into db2 using Db2Dao.
My ExecuteClass looks like this:
class ExecuteClass {
private Db1Dao db1Dao;
private Db2Dao db2Dao;
public void execute() {
...
List<String> listOfString = getExternalData(someParam);
List<Metadata> metadatum = db1Dao.get(someInputs);
... I do something to generate a list of new class `A` based on listOfString & metadatum ...
try {
db2Dao.put(listOfA);
} catch (PutException e){
...
}
}
public List<String> getExternalData(SomeClass someParam){
... do something
return listOfString;
}
}
Now I want to test:
Given a specific listOfString (returned by getExternalData) and a specific metadatum (returned by db1Dao.get):
Will I get the desired listOfA?
Am I able to call db2Dao.put and its input parameter is listOfA?
Particularly, I have hard-coded sample listOfString and metadatum and desired listOfA (and they will be passed via an object MockData, see the following code) but I don't know how to write the test using Mockito. The following is a test class I wrote but it does not work:
class TestClass extends BaseTest {
#Mock
private Db1Dao db1Dao;
#Mock
private Db2Dao db2Dao;
private ExecuteClass executeClass;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
executeClass = new ExecuteClass(db1Dao, db2Dao);
}
#ParameterizedTest
#MethodSource("MockDataProvider")
public void executeClassTest(final MockData mockData) throws PutException {
Mockito.when(db1Dao.get(Mockito.any(), ...))
.thenReturn(mockData.getMetadatum());
ExecuteClass executeClassSpy = Mockito.spy(executeClass);
Mockito.when(executeClassSpy.getExternalData(Mockito.any()))
.thenReturn(mockData.getListOfString());
executeClassSpy.execute();
// executeClass.execute(); not working neither...
List<A> listOfA = mockData.getDesiredListOfA();
Mockito.verify(db2Dao).put(listOfA);
}
}
Could anyone please let me know? Thank you in advance!!
You should not create a spy of the same class you want to test. Instead, try to write a unit test for the smallest amount of code (e.g. a public method) and mock every external operator (in your case Db1Dao and Db2Dao).
If testing a public method involves calling another public method of the same class, make sure to mock everything inside the other public method (in your case getExternalData). Otherwise, this other public method might be a good candidate for an extra class to have clear separation of concerns.
So, remove the ExecuteClass executeClassSpy = Mockito.spy(executeClass); and make sure you setup everything with Mockito what's called within getExternalData.
To now actually, verify that Db2Dao was called with the correct parameter, either use your current approach with verifying the payload. But here it's important to 100% create the same data structure you get while executing your application code.
Another solution would be to use Mockito's #Captor. This allows you to capture the value of why verifying the invocation of a mock. Later on, you can also write assertions on the captured value:
#Captor
private ArgumentCaptor<ClassOfListOfA> argumentCaptor;
#Test
public void yourTest() {
Mockito.verify(db2Dao).put(argumentCaptor.capture());
assertEquals("StringValue", argumentCaptur.getValue().getWhateverGetterYouHave);
}
The following code worked for me.
I partially accepted #rieckpil's answer. I used #Captor which is very handy.
The reason I had to mock getExternalData() is because its implementation is still a "TODO".
class TestClass extends BaseTest {
#Mock
private Db1Dao db1Dao;
#Mock
private Db2Dao db2Dao;
#Captor
private ArgumentCaptor<List<A>> argumentCaptor;
private ExecuteClass executeClass;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
executeClass = new ExecuteClass(db1Dao, db2Dao);
}
#ParameterizedTest
#MethodSource("MockDataProvider")
public void executeClassTest(final MockData mockData) throws PutException {
Mockito.when(db1Dao.get(Mockito.any(), ...))
.thenReturn(mockData.getMetadatum());
ExecuteClass executeClassSpy = Mockito.spy(executeClass);
Mockito.when(executeClassSpy.getExternalData(Mockito.any()))
.thenReturn(mockData.getListOfString());
executeClassSpy.execute();
List<A> listOfA = mockData.getDesiredListOfA();
Mockito.verify(db2Dao).put(argumentCaptor.capture());
assertEquals(listOfA, argumentCaptor.getValue());
}
}
I have an interface:
public interface Service {
void doService();
}
And a class:
public class ServiceUser {
#Inject
#Any
private Instance<Service> serviceImplementations;
public void work() {
serviceImplementations.forEach(service -> service.doService());
}
}
And a testcase:
public class ServiceUserTest {
#Mock
private Service firstImpl;
#Mock
private Service secondImpl;
#InjectMocks
private ServiceUser serviceUser;
#Test
public void testAllImplementationsCalled() {
serviceUser.work();
verify(firstImpl).doService();
verify(secondImpl).doService();
}
}
When I run the testcase, I get a NullPointerException in ServiceUser, where on debugging serviceImplementations is null. If I create two fields in ServiceUser I can get the two instances injected (both mocks are injected, I checked during debugging).
public class ServiceUser {
#Inject
#Any
private Instance<Service> serviceImplementations;
#EJB
private Service firstImpl;
#EJB
private Service secondImpl;
public void work() {
serviceImplementations.forEach(service -> service.doService());
}
}
How can I make this work?
Surprisingly, I couldn't find a good existing fake or test double for Instance<T>, as in Instance<Service> serviceInstance = TestingInstance.of(firstImpl, secondImpl);.
You might want to create one—not using Mockito, but rather as an actual implementation—and test and use it. The interface is relatively small, particularly if you just throw UnsupportedOperationException on the select methods, and then you'll have an implementation you can use in all of your test cases.
Because it's not a mock, you would need an alternative to #InjectMocks, though; given the fragility of #InjectMocks as dependencies change, this may not be a bad thing anyway.