I am unable to understand few things while testing with JUnit, I have written a few tests, while some seem to work simply with
#RunWith(MockitoJUnitRunner.class)
and
#Mock
some seem not to work with them, and so I have to use
#RunWith(SpringRunner.class)
and
#MockBean
I understand that #MockBean is used when dealing with Spring Container, while #Mock is just to use to replicate/mock a certain class and its methods. But what would be the perfect time to use #MockBean?
#Test
public void addBulkFcmLog() throws Exception {
JacksonTester.initFields(this, new ObjectMapper());
AdminFcmResource adminFcmResource = AdminFcmResource.builder()
.adminId(123L)
.build();
given(this.fcmService.addBulkFcmLog(any(BulkFcmDataResource.class))).willReturn(adminFcmResource);
MockHttpServletResponse response = mockMvc.perform(
post(Routes.SEND_FCM + "/admin/logs/add")
.contentType(MediaType.APPLICATION_JSON)
.content(bulkFcmDataResourceJacksonTester.write(BulkFcmDataResource.builder().adminId(123L).build()).getJson()))
.andExpect(status().isCreated())
.andReturn()
.getResponse();
assertThat(response.getContentAsString()).isEqualTo(
adminFcmResourceJacksonTester.write(adminFcmResource).getJson()
);
assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value());
}
Above is a test I wrote for a controller in my spring boot application, but When i mock the fcmService class using #Mock, the response comes with an empty body, but then I changed It to #MockBean and Autowired the controller class then test returned the right result.
How did this happen?
In an integration-test context it is always advisable to work with beans (mocked if necessary). Few points to keep in mind:
#MockBean takes care of injecting the object, you do not need to set it yourself.
If your configuration is wrong (you did not specify a #Qualifier for example), the test will expose that problem on startup.
In an integration test you want to have your system in a state which resembles the prod scenario as close as possible, and #MockBean gets you closer than a plain #Mock
Many times the bean dependencies have no setters and manual injection would be just plain hard.
Related
I want to create JUnkt test for this endpoint:
#Autowired
private JwtTokenProvider jwtTokenProvider;
#PostMapping("reset_token")
public ResponseEntity<?> resetToken(#Valid #RequestBody ResetPasswordTokenDTO resetPasswordTokenDTO, BindingResult bindResult) {
final String login = jwtTokenProvider.getUsername(resetPasswordTokenDTO.getResetPasswordToken());
}
Full code: Github
JUnit test:
#Test
public void resetTokenTest_NOT_FOUND() throws Exception {
when(usersService.findByResetPasswordToken(anyString())).thenReturn(Optional.empty());
mockMvc.perform(post("/users/reset_token")
.contentType(MediaType.APPLICATION_JSON)
.content(ResetPasswordTokenDTO))
.andExpect(status().isNotFound());
}
I get NPE at this line when I run the code:
final String login = jwtTokenProvider.getUsername(resetPasswordTokenDTO.getResetPasswordToken());
How I can mock jwtTokenProvider properly? As you can see I have a file with test data which I load but the token is not extracted. Do you know how I can fix this issue?
The most straightforward way is to use Mockito and create mock instances and pass it directly to your controller class using constructor injection.
However, if you do not wish to use constructor injection (I recommend you to use it though, as it is much more explicit) you need to define your beans in a separate test configuration class
#Profile("test")
#Configuration
public class TestConfiguration {
#Bean
public JwtTokenProvider mockJwtTokenProvider() {
return Mockito.mock(JwtTokenProvider.class);
}
}
Also, add the correct profile to your test class by #ActiveProfiles("test")
You can consider using a #MockBean directly in your test class to mock your JwtTokenProvider. #MockBean annotation is Spring-ish and is included in spring-boot-starter-test. The Spring Boot documentation summarizes it well:
Spring Boot includes a #MockBean annotation that can be used to define
a Mockito mock for a bean inside your ApplicationContext. You can use
the annotation to add new beans or replace a single existing bean
definition. The annotation can be used directly on test classes, on
fields within your test, or on #Configuration classes and fields. When
used on a field, the instance of the created mock is also injected.
Mock beans are automatically reset after each test method.
The #MockBean annotation will make Spring look for an existing single bean of type JwtTokenProvider in its application context. If it exists, the mock will replace that bean, and if it does not exist, it adds the new mock in the application context.
Your test class would look like this:
import org.springframework.boot.test.mock.mockito.MockBean;
#MockBean
#Qualifier("xxx") //If there is more than one bean of type JwtTokenProvider
private JwtTokenProvider jwtTokenProvider;
#Test
public void resetTokenTest_NOT_FOUND() throws Exception {
when(jwtTokenProvider.getUsername(anyString())).thenReturn(Optional.empty());
mockMvc.perform(post("/users/reset_token")
.contentType(MediaType.APPLICATION_JSON)
.content(ResetPasswordTokenDTO))
.andExpect(status().isNotFound());
}
You might also want to check this and this.
In a project where I work, we have been initializing Services for Unit testing in the following way:
Mock dependencies that are needed for the Service.
Create the Service using a constructor.
Something like this :
#RunWith(SpringRunner.class)
public class ServiceTest extends AbstractUnitTest {
#Mock private Repository repository;
private Service service;
#Before
public void init() {
service = new Service(repository);
when(repository.findById(any(Long.class))).thenReturn(Optional.of(new Entity()));
}
}
But our new developer proposed to use #Autowired and #SpringBootTest
#SpringBootTest(classes = ServiceTest.class)
#MockBean(classes = Repository.class)
#RunWith(SpringRunner.class)
public class ServiceTest extends AbstractUnitTest {
#MockBean private Repository repository;
#Autowired private Service service;
#Before
public void init() {
when(repository.findById(any(Long.class))).thenReturn(Optional.of(new Entity()));
}
}
Before that, I supposed that #Autowired and #SpringBootTest should be used only in integration tests. But googled a lot and I see that some people use those two in Unit tests.
I read boot-features-testing. Also, I read this Unit tests vs integration tests with Spring.
For me, it still doesn`t feel well that we need to involve Spring to do dependency injection for unit tests, as we can do this by ourselves to do the unit testing.
So, should #Autowired and #SpringBootTest be used in unit tests?
No. A unit test is to test a single component in isolation. Using constructor injection in your beans allows you to very simply call new SomeService(myMock), no Spring required.
Writing component or functional tests (testing your application but not wiring it up to external services for a full integration test, mocking only external interfaces; this is good for things like MockMvc tests) is a good match for #SpringBootTest, and in that case you may need to create mock objects in a Spring configuration and autowire them into your test so you can manipulate them.
In TDD tests should be helpful, straight forward, fast and keep maintenance at minimum. Otherwise devs will be annoyed and try to avoid tests.
So I recommend not to be to strict if it's a pure unit-test or a bit of a integration. Choose the test-scope that suits best for your situation and use the technical features that fit into this scope.
Don't use DI if you are doing a "real" unittest, testing a self-contained method on its own. Those tests make sense if the method does something meaningful, e.g. algorithm, calculation, decision making. Mocking sources of data is great here to get predictible input values.
The downside of a #SpringBootTest that you don't really need is a hell of a startup-time (depends on project size) which is really annoying.
Use CDI if a method calls functionality on a dependency. 1) Setting myService.service2 = new Service2() by hand leaves you with a Service2 that is also not handled by a DI-Container which maybe requires you to set some more dependencies... 2) CDI in testing is a breeze with Spring - so why would you bloat your tests with setup-code? 3) DI involves proxies which sometimes behave a little different from simple instances.
Use #ContextConfiguration(classes = {ServiceTest.class}) to get CDI with a faster startup compared to #SpringBootTest.
Don't test glueing code with unittests as it does not have any intrinsic value. Those tests are hard to understand (who loves documenting a test?), will require a lot of mocking and will often be subject to change. Test such code in association with other methods/classes even if it means you only have integration tests for these parts of the code.
I'm attempting to test a REST controller (using Quarkus) endpoint using rest assured. I want to mock a class that is injected into that controller (ideally with Mockio), but only for one of my tests. Or get different behaviour per test case without having to have separate classes for each test. I'm not sure how to do this?
I've seen doing it the way from the documentation:
#Mock
#ApplicationScoped
public class MockExternalService extends ExternalService {
#Override
public String service() {
return "mock";
}
}
But this would only allow me to use one mock for all tests and not mock certain behaviours based on tests as I would with Mockito. I think?
I've tried creating a mock and annotating it with #Mock
#Mock
public TableExtractorService tableExtractorServiceMock = Mockito.mock(TableExtractorService.class);;
but I still get my real implementation when I use it. I'm using a constructor annotated with #Inject in my Controller that takes the TableExtractorService.
For a bit more information my test using restassured looks like this:
InputPart filePart = Mockito.mock(InputPart.class);
Mockito.when(tableExtractorServiceMock.Extract(anyObject()))
.thenThrow(IOException.class);
final InputStream inputStream = filePart.getBody(InputStream.class, null);
given()
.multiPart("file", inputStream)
.when().post("/document")
.then()
.statusCode(500);
That endpoint calls the service class that I'm trying to mock, and I want that mock to return an exception.
It can't be done. Quarkus documentation explains the issue:-
Although this mechanism is fairly straightforward to use, it nonetheless suffers from a few problems:
A new class (or a new CDI producer method) needs to be used for each bean type that requires a mock. In a large application where a lot of mocks are needed, the amount of boilerplate code increases unacceptably.
There is no way for a mock to be used for certain tests only. This is due to the fact that beans that are annotated with #Mock are normal CDI beans (and are therefore used throughout the application). Depending on what needs to be tested, this can be very problematic.
There is a no out of the box integration with Mockito, which is the de-facto standard for mocking in Java applications. Users can certainly use Mockito (most commonly by using a CDI producer method), but there is boilerplate code involved.
Link for reference: https://quarkus.io/blog/mocking/
According Quarkus test documentation, you can do it usingo #QuarkusMock or #InjectMock.
As #Ankush said, a class annotated with the #Mock annotation is using the CDI #Alternative mechanism, and will be global. #QuarkusTestProfiles can be used to define CDI #Alternatives for groups of tests.
For example, instead of annotating the mock with #Mock, it could be referenced in a test profile as
default Set<Class<?>> getEnabledAlternatives() {
return Set.of(MyMockThing.class);
}
Any test annotatated with the
#TestProfile(MyMockyTestProfile.class)
profile would get those mocks, while others would use the original implementation.
What may be a simpler method is to just use #InjectMock. For example, in the test class, declaring a field like this:
#InjectMock
MyThing mock;
will ensure that mock is used by the classes under test, just for this test.
For rest clients, it will also be necessary to add a #RestClient annotation, and if the original implementation is a singleton, convertscopes can be used to coax the scopes into something mockable.
#RestClient
#InjectMock(convertScopes = true)
MyThing mock;
Behaviour can be added to the injected mock in #BeforeEach or #BeforeAll methods. For example
#BeforeEach
public void setup() {
when(mock.someMethod()).thenReturn("some value");
}
I am getting null pointer exceptions on my transaction template when I try to test my method that uses manual transactions. When I am running the application in Spring Boot it works as expected.
#Autowired
TransactionTemplate template;
public CompletableFuture<MyResultEntity> addToA(BInput input) {
return CompletableFuture
.supplyAsync(
() -> template.execute(status -> {
A a = aRepository.findOne(input.getA());
List<B> addedBs = saveBs(input.getB(), a);
return new MyResultEntity(a, addedBs);
}), MyCustomExecutor());
}
I have tried using a mock template and inject it like this:
#Mock
private TransactionTemplate transactionTemplate;
#InjectMocks
private MyClass myClass;
I have also tried annotating my test with:
#RunWith(SpringJUnit4ClassRunner.class)
When debugging this configuration the template is actually injected and is not null any more. But since I am interested in testing the actions in the transactions I do not wish to mock it so i use:
when(transactionTemplate.execute(Mockito.any())).thenCallRealMethod();
This throws a new null pointer exception though since the transaction template tries to use the TransactionManager and that is still null.
How can I unit test my method calls inside the the transaction template?
What I normally do is not to call the real method, but to just mock the real behavior instead. Calling the real method in the mock will fail, because a mock is not managed inside of springs injection context. Well, to be exact, you can make them exist inside the injection context by adding them to a test configuration (plain SpringMVC) or using #MockBean (spring boot). But still they are just injected as a dependency. But won't receive any dependencies. For unit tests this is most often the desired behavior.
So just do something like:
when(_transactionTemplate.execute(any())).thenAnswer(invocation -> invocation.<TransactionCallback<Boolean>>getArgument(0).doInTransaction(_transactionStatus));
_transactionStatus can be a mock itself to test the usage of the state inside the callback.
Mocking is what mocks are used for :)
If TransactionManager is null it means that Spring probably didn't load all necessary dependencies in the test context.
Anyway, why mocking the TransactionTemplate if you need to invoke the execute() method ?
Your test looks like an integration/sociable test and not an unit test.
If it is the case, you don't need to mock anything.
If you want to write an unit test that tests the actual logic in the addToA() method, you should use mock without partial mocking.
Mock dependencies used in the Supplier provided and assert that the expected MyResultEntity instance is returned.
Note that your unit test will have a limited value and may be considered as brittle as it asserts only that a series of method was invoked. Generally you want to assert a behavior in terms of more concrete logic such as computation/extraction/transformation.
Here is an example (no tested but it should give an idea on the way) :
#Mock
Repository ARepositoryMock;
#Mock
Repository BRepositoryMock;
#Test
public void addToA() throws Exception {
BInput input = new BInput();
// record mock behaviors
A aMockByRepository = Mockito.mock(A.class);
List<B> listBMockByRepository = new arrayList<>();
Mockito.when(ARepositoryMock.findOne(input.getA())).thenReturn(aMockByRepository);
Mockito.when(BRepositoryMock.saveBs(input.getB(), aMockByRepository)).thenReturn(listBMockByRepository);
// action
CompletableFuture<MyResultEntity> future = myObjectUnderTest.addToA(input);
//assertion
MyResultEntity actualResultEntity = future.get();
Assert.assertEquals(aMockByRepository, actualResultEntity.getA());
Assert.assertEquals(listBMockByRepository, actualResultEntity.getListOfB());
}
I am trying to mock a local object, using Mockito Framework, that is being created from the spring application context; But every time I run the application its fails to replace the original object with the mocked object.
Here is the original class's code spinets:
public void executeMyJob(){
ApplicationContext springContext = ApplicationContextUtil.getApplicationContext();
MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO");
List<Brand> dataList =music.getAll();
......
}
I want to mock the MusicEAO so when the getAll() method is called, it will use the mock object.
Below is my test class code snippets:
#Mock
MusicEAO musicEAO;
when(musicEAO.findAll()).thenReturn(myDefinedList);
How can I resolve this issue?
It's hard to tell from the cod you posted but the problem might be that you are mocking MusicEAO in your test but the code you are executing is using a Spring ApplicationContext to get a reference to the MusicEAO bean.
Your original class should not use MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO"); but instead have the bean injected by Spring using #Autowired through a constructor or a setter method (or other dependency injection method). You test will then be able to easily inject a mock version.
class MyJobClass {
MusicEAO music;
#Autowired
public MyJobClass(MusicEAO musicEao) {
this.music = musicEao;
}
public void executeMyJob(){
List<Brand> dataList =music.getAll();
......
}
}
When you say
every time I run the application it fails to replace the original
object with the mocked object
You shouldn't have to run the application to run a unit test for this class - are you asking how to inject mocks into a running application?
.
It doesn't work this way. In your current code :
The following instance is the one in your test :
#Mock MusicEAO musicEAO;
But in your production code, you are using Spring to acquire the Music instance :
MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO");
Nowhere you seem to say to spring that you want the Music mock to be affected to MusicEAO bean name.
If you are doing a Unit Test I would recommand you to avoid messing with Spring, if that's an integration test, then you'll have to find a way to create the mock and pass it over to spring.
For example you can create the mock in the Spring context, autowire it in your test.
Also, I wouldn't recommand the use of static calls in this situation to acquire the Spring context in order to finaly get a hold on the Music object. it makes me think of Spring as a registry which isn't really the case. Spring is a container. Instead you should try to refactor your code in such a way that the Music bean is wired (#Autowired, setters, etc.) in ExecutionJob.
Then it would be even easier to write a Unit Test with JUnit and Mockito, with annotations like #Mock and #InjectMocks.
Hope that helps.