I'm unit testing a Spring boot web app with Mockito. One of my methods is returning a void, but if I try to test it, I get compilation errors.
This is the test I wrote:
public void testDeleteActor()throws NetflixException {
when(actorRepository.findById(1L)).thenReturn(Optional.of(Mockito.any(Actor.class)));
assertEquals(null, service.deleteActorById(Mockito.anyLong());
}
And this is the method I'm trying to test:
#Override
public void deleteActorById(Long id) throws NetflixException {
Actor actor = actorRepository
.findById(id)
.orElseThrow(() -> new NotFoundException("Actor id not found - " + id));
actorRepository.delete(actor);
}
As you can see in the following screenshot, I'm getting an error with my assertEquals() statement:
In your code, your method isn't actually returning null, it's returning nothing (void). That means that you can't write assertions based on what the method returns. This is the reason why the assertEquals() statement is giving you an error.
In stead of testing what the method returns, you can test the expected behaviour of the method. In this example, there are three things we expect:
The method should retrieve the actor by its ID.
The method should throw an exception if no actor was found with the given ID.
The method should delete the actor if it was found.
To implement this these tests, you can use Mockito's verify() and AssertJ's assertThatExceptionOfType(). For example:
#Test
void deleteActorById_retrievesActorByID() {
Actor actor = new Actor();
when(actorRepository.findById(1L)).thenReturn(Optional.of(actor));
service.deleteActorById(1L);
verify(repository).findById(1L);
}
#Test
void deleteActorById_throwsExceptionIfIDNotFound() {
assertThatExceptionOfType(NotFoundException.class)
.isThrownBy(() -> service.deleteActorById(1L))
.withMessage("Actor id not found - 1");
}
#Test
void deleteActorById_deletesActorIfIDFound() {
Actor actor = new Actor();
when(actorRepository.findById(1L)).thenReturn(Optional.of(actor));
service.deleteActorById(1L);
verify(actorRepository).delete(actor);
}
Related
First - I've checked all previous topics around this question and none of them helped.
Having the following code:
#DisplayName("GET RecipeUltraLight by id is successful")
#Test
public void givenRecipeId_whenGetRecipeDetailsById_thenReturnRecipeObject(){
// given
given(this.recipeRepository.findById(recipe.getId())).willReturn(Optional.of(recipe));
given(this.recipeService.getRecipeById(recipe.getId())).willReturn(recipe);
given(this.recipeConverter.toUltraLight(recipe)).willReturn(recipeUltraLightDto);
// when
RecipeUltraLightDto retrievedRecipe = recipeService.getRecipeUltraLightById(recipe.getId());
// then
verify(recipeRepository, times(1)).findById(recipe.getId());
verify(recipeService, times(1)).getRecipeById(recipe.getId());
verify(recipeConverter, times(1)).toUltraLight(recipe);
assertThat(retrievedRecipe).isNotNull();
}
gives me this error:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Recipe cannot be returned by findById()
findById() should return Optional
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
Service method:
#Transactional(readOnly = true)
public RecipeUltraLightDto getRecipeUltraLightById(Long id) {
Recipe recipe = getRecipeById(id);
RecipeUltraLightDto dto = new RecipeUltraLightDto();
dto = recipeConverter.toUltraLight(recipe);
return dto;
}
// internal use only
#Transactional(readOnly = true)
public Recipe getRecipeById(Long id) {
if (id == null || id < 1) {
return null;
}
return recipeRepository.findById(id)
.orElseThrow(() -> new RecipeNotFoundException(
String.format("Recipe with id %d not found.", id)
));
}
Setup:
#ContextConfiguration(classes = {RecipeService.class})
#ExtendWith({SpringExtension.class, MockitoExtension.class})
class RecipeServiceTest {
#MockBean
private RecipeConverter recipeConverter;
#MockBean
private RecipeRepository recipeRepository;
#Autowired
private RecipeService recipeService;
private Recipe recipe;
private RecipeUltraLightDto recipeUltraLightDto;
#BeforeEach
public void setup(){
recipe = Recipe.builder()
.id(1L)
.name("Recipe")
.description("Description")
.createdAt(LocalDateTime.now())
.difficulty(RecipeDifficulty.EASY)
.minutesRequired(60)
.portions(4)
.authorId(1L)
.views(0)
.isVerified(false)
.build();
recipeUltraLightDto = RecipeUltraLightDto.builder()
.id(1L)
.name("Recipe")
.build();
}
I've tried:
Optinal.ofNullable()
Adding .isPresent()
Getting rid of .orElseThrow and going through if statements and using .get()
Kotlin
Will be glad if someone can help.
You are creating a mock of the object you are testing and with that basically also render the mocking of the repository useless.
You should remove the line given(this.recipeService.getRecipeById(recipe.getId())).willReturn(recipe); that way it will just call the method and call the repository. Which now will return the mocked result. As that is the behavior that will now kick in.
It is clearly mentioned that the method findById() returning Optional, you need to get Recipe by invoking Optional.get().
I'm trying to unit test my GatewayFilter, however I'm having troubles running even simple test.
This is small example of what is failing right now
#ExtendWith(MockitoExtension.class)
public class SomeFilterTest {
private final GatewayFilter gatewayFilter = (exchange, chain) ->
Mono.just("Hello")
.flatMap(this::doSomething)
.switchIfEmpty(Mono.defer(() -> chain.filter(exchange)));
private Mono<Void> doSomething(String value) {
System.out.println(value);
return Mono.empty();
}
#Test
void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(GatewayFilterChain.class);
gatewayFilter.filter(exchange, chain).block();
}
}
Unfortunatelly, it is failing because of
The Mono returned by the supplier is null
java.lang.NullPointerException: The Mono returned by the supplier is
null at java.base/java.util.Objects.requireNonNull(Objects.java:246)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) at
reactor.core.publisher.Mono.subscribe(Mono.java:4361)
And to be honest, I have no idea why is that happening?
You have not stubbed out the filter method call on your mock object, GatewayFilterChain. As a result, the supplier () -> chain.filter(exchange) returns null. You are not allowed to create a Mono with a value of null, hence the exception.
As a result your test should look something like
#Test
public void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(WebFilterChain.class);
// stubbing behaviour on our mock object
given(chain.filter(exchange)).willReturn(Mono.empty());
gatewayFilter.filter(exchange, chain).block();
}
Additionally, I would suggest using StepVerifier instead of using block() in unit tests. This is provided by reactor-test and is purpose built for unit testing reactive code
#Test
public void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(WebFilterChain.class);
given(chain.filter(exchange)).willReturn(Mono.empty());
StepVerifier.create(gatewayFilter.filter(exchange, chain))
.verifyComplete();
}
Here is a very useful Step Verifier Tutorial to help you get started
I'm unit testing a Spring boot web app with Mockito. One of my methods is returning a Actorrest, but if I try to test it, I get compilation errors.
This is the test I wrote:
#Test
void AddActorToChapters() throws NetflixException{
when(actorRepository.findById(MockData.getActor().getId())).thenReturn(Optional.of(MockData.getActor()));
when(chapterService.findChapterById(MockData.getChapter().getId())).thenReturn((MockData.getChapter()));
service.AddActorToChapter(MockData.getChapter().getId(), MockData.getActor().getId());
assertEquals(MockData.getActor().getChapters(), MockData.getChapter());
}
And this is the method I'm trying to test:
#Override
public ActorParticipant AddActorToChapter(Long idActor, Long idChapter) throws NetflixException {
Actor actor = actorRepository.findById(idActor)
.orElseThrow(() -> new NotFoundException("Actor id not found - " + idActor));
if (actorRepository.existsByIdAndChapters_Id(idActor, idChapter)) {
throw new DuplicateException("Actor id found - " + idActor);
}
Chapter chapter = chapterService.findChapterById(idChapter);
actor.getChapters().add(chapter);
actorRepository.save(actor);
ActorParticpateMapper mapper = new ActorParticpateMapper();
return mapper.mapActorToActorParticipant(actor);
}
and the database mockdata are
public class MockData {
public static Actor getActor() {
Actor actor = new Actor();
actor.setId(1L);
actor.setName("ali");
actor.setNationality("ameriacn");
actor.getChapters().add(getChapter());
return actor;
}
public static Chapter getChapter() {
Chapter chapter = new Chapter();
chapter.setId(1L);
chapter.setName("Chapter 7");
return chapter;
}}
log error
java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=AddActorToChapters], {ExactMatcher:fDisplayName=AddActorToChapters(actortest.ActorServiceTesting)], {LeadingIdentifierMatcher:fClassName=actortest.ActorServiceTesting,fLeadingIdentifier=AddActorToChapters]] from org.junit.internal.requests.ClassRequest#64b8f8f4
at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:40)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createFilteredTest(JUnit4TestLoader.java:83)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:74)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:49)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:513)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
With the JUnit 4 test runner you are using, the test methods must be defined as public.
I am using Smallrye Mutiniy reactive library in my Quarks application as it is supported natively in Quarks applications.
I'am trying to write unit tests for a service class. I am not sure how to write unit tests for a method that returns Uni/Multi.
A method returning Uni<String>
public Uni<String> hello(final String name) {
final String message = "Hello " + name;
return Uni.createFrom().item(message);
}
Unit implemented for the above method
#Test
void testHello() {
final Uni<String> casePass = hello("Ram");
// assertion passes and all good with this.
casePass.subscribe().with(message -> Assertions.assertEquals("Hello Ram", message));
final Uni<String> caseFail = hello("Ravan");
// It is expected to fail the assertion, and it does. But the test is not failing, instead aseertion fail message simply logged to the console.
caseFail.subscribe().with(message -> Assertions.assertEquals("Hello Sita", message));
}
Console logs
[-- Mutiny had to drop the following exception --]
Exception received by: io.smallrye.mutiny.helpers.UniCallbackSubscriber.onItem(UniCallbackSubscriber.java:71)
org.opentest4j.AssertionFailedError: expected: <Hello Sita> but was: <Hello Ram>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1124)
...
There are several approaches. You can use the utility methods provided in smallrye.io/smallrye-mutiny/guides/testing. However, if, according to your comment, you need more, I would recommend the following approach:
final Uni<Greeting> casePass = hello("Ram");
Greeting g = casePAss.await().atMost(Duration.ofSeconds(5)); // To be sure we don't stay tucked
// Assertions come here
So basically, you block until the item is sent. Avoid await().indefinitely() because it may block your test if the Uni never send an item. Not that await()... throws an exception if the Uni sends a failure.
I would really recommend using the way of testing from SmallRey.
https://smallrye.io/smallrye-mutiny/1.7.0/guides/testing/
You still can get the object out of the multi/uni
use invoke, for example.
public static Uni<String> hello(final String name) {
final String message = "Hello " + name;
return Uni.createFrom().item(message);
}
#Test
public void testUnit() {
UniAssertSubscriber<String> tester = hello("someone")
.invoke( i -> Assertions.assertEquals("Hello someone", i))
.invoke(i -> Assertions.assertNotNull(i))
.subscribe().withSubscriber(UniAssertSubscriber.create());
tester.assertCompleted();
}
#Test
public void secondUnit() {
UniAssertSubscriber<String> tester = hello("none")
.invoke( i -> Assertions.assertEquals("Hello someone", i))
.invoke(i -> Assertions.assertNotNull(i))
.subscribe().withSubscriber(UniAssertSubscriber.create());
tester.assertCompleted();
}
I hope you can use it like that
I have a method in OneServiceImpl class as follows. In that class I am calling an interface method from another class.
public class OneServiceImpl {
//created dependency
final private SecondService secondService;
public void sendMessage(){
secondService.validateAndSend(5)
}
}
public interface SecondService() {
public Status validateAndSend(int length);
}
public class SecondServiceImpl {
#Override
public Status ValidateAndSend(int length) {
if(length < 5) {
throw new BadRequestException("error", "error");
}
}
}
Now when I am try to perform unit test on OneServiceImpl I am not able to throw a BadRequestException.
when(secondService.validateAndSend(6)).thenThrow(BadRequestException.class);
Not quite sure what your use case is, but I think you should write an own test to accept and test an exception.
#Test(expected = BadRequestException.class)
public void testValidateAndSend(){
SecondService secondService = new SecondService();
secondservice.ValidateAndSend(6); //method should be lowercase
}
Not sure this is the case considering you didn't post a full example of code + unit tests, but your mock will throw only when you are passing 6 as parameter. When configuring the behaviour of your mock with when you are telling it to throw only when the validateAndSend method is called with parameter 6.
when(secondService.validateAndSend(6)).thenThrow(...)
In your code you have 5 hardcoded. So that mock will never throw for the code you have, because it's configured to react to an invocation with parameter 6 but the actual code is always invoking it passing 5.
public void sendMessage(){
secondService.validateAndSend(5)
}
If the value passed to the mock is not important you could do something like the following, that will throw no matter what's passed to it:
when(secondService.validateAndSend(any())).thenThrow(BadRequestException.class);
On the other hand, if the value is important and it has to be 5 you could change the configuration of your mock with:
when(secondService.validateAndSend(5)).thenThrow(BadRequestException.class)