Mockito throws UnfinishedStubbingException for no apparent reason - java

I am trying to write a "simple" unit test. Mockito however always tells me there is an UnfinishedStubbingException.
The code line Mockito is exposing as culprit is the following:
when(myServiceIdFactory.get(any())).thenReturn((SortedSet<MyServiceId>) Set.of(emptyId));
Here is the whole unit test code
#SpringBootTest
#RunWith(SpringRunner.class)
public class MyServiceIdProcessorTest {
#Autowired
private MyServiceIdProcessor myServiceIdProcessor;
#MockBean
private MyServiceIdFactory myServiceIdFactory;
#Test
public void shouldFilterProductsWithNoId() {
Product productWithNoId = new Product();
MyServiceId emptyId = new MyServiceId();
when(myServiceIdFactory.get(any())).thenReturn((SortedSet<MyServiceId>) Set.of(emptyId));
CatalogDTO catalogDTO = new CatalogDTO();
Envelope<CatalogDTO, Product> envelopeToTest = Envelope.products(List.of(productWithNoId));
Envelope returnedEnvelope = myServiceIdProcessor.enrichCatalog(envelopeToTest);
assertThat(returnedEnvelope.getProducts()).hasSize(0);
}
}

The problem was the following cast exception:
when(myServiceIdFactory.get(any())).thenReturn((SortedSet<MyServiceId>) Set.of(emptyId));
Set.of(foo) can't be casted to a SortedSet. The exception however seemed to be swallowed and overwritten by Mockito

Try to spy() the MyServiceIdFactory before define the bahavoir
#Test
public void shouldFilterProductsWithNoId() {
// Arrange
Product productWithNoId = new Product();
MyServiceId emptyId = new MyServiceId();
MyServiceIdFactory spyMyServiceIdFactory = spy(myServiceIdFactory);
SortedSet<MyServiceId> set = (SortedSet<MyServiceId>) Set.of(emptyId);
doReturn(set).when(spyMyServiceIdFactory).get(any());
CatalogDTO catalogDTO = new CatalogDTO();
Envelope<CatalogDTO, Product> envelopeToTest = Envelope.products(List.of(productWithNoId));
// Act
Envelope returnedEnvelope = spyMyServiceIdFactory.enrichCatalog(envelopeToTest);
// Assert
assertThat(returnedEnvelope.getProducts()).hasSize(0);
}

Related

PowerMock not creating mock for new final object instance

I am writing an unit test for method that instantiates final class like this:
public Mono<String> getSecret(String key) {
SecretClientBuilder secretClientBuilder = new SecretClientBuilder();
secretClientBuilder = secretClientBuilder.vaultUrl(keyVaultUrl);
secretClientBuilder = secretClientBuilder.credential(new DefaultAzureCredentialBuilder().build());
SecretClient secretClient = secretClientBuilder.buildClient();
KeyVaultSecret retrievedSecret = secretClient.getSecret(key);
return Mono.just(retrievedSecret.getValue());
}
SecretClient and SecretClientBuilder are imported from azure library. In order to test this, I am trying to use PowerMockito
My approach is to create mock object when new SecretClientBuilder() is called so when that mock object is calling methods such as .vaultUrl() or .credential(), I can make my test to return a mocked objects. Here is my starting code:
#Before
public void before() {
keyVaultService = new KeyVaultServiceImpl(KEY_VAULT_URL);
}
#Test
public void test() throws Exception {
SecretClientBuilder secretClientBuilder = mock(SecretClientBuilder.class);
PowerMockito.whenNew(SecretClientBuilder.class).withNoArguments().thenReturn(secretClientBuilder);
PowerMockito.when(secretClientBuilder.vaultUrl(anyString())).thenReturn(secretClientBuilder);
keyVaultService.getSecret(CLIENT_ID_KEY);
}
However, test fails because when SecretClientBuilder is being instantiated, it does not return the mock object. Instead, it creates a new instance of SecretClientBuilder when SecretClientBuilder secretClientBuilder = new SecretClientBuilder().
How can I instantiate SecretClientBuilder object using PowerMockito? SecretClientBuilder is final class.
Edit:
This is what I am using for class annotations:
#RunWith(PowerMockRunner.class)
#PrepareForTest({KeyVaultService.class, SecretClientBuilder.class})
I tried only using either classes in #PrepareForTest and nothing works with same behavior.
Edit2: I was testing with TestService to see if final was an issue. It was not. Here is my TestService:
public class TestService {
public String test() {
return "actual";
}
and here is my KeyVaultService:
public Mono<String> getSecret(String key) {
TestService testService = new TestService();
System.out.println(testService.test());
...
}
and my test:
#Test
public void test() throws Exception {
TestService mock = mock(TestService.class);
PowerMockito.whenNew(TestService .class).withAnyArguments().thenReturn(mock);
PowerMockito.when(mock.test()).thenReturn("mock");
keyVaultService.getSecret(CLIENT_ID_KEY);//should print "mock"
}
But it prints "actual" meaning mocked object was never created when new TestService(); was called. Is my understanding of PowerMockito and whenNew wrong somewhere?
Found an answer. Leaving this for anybody who is having some issue.
Problem was that I was adding interface class not actual implementation class. Changing #PrepareForTest(KeyVaultService.class) to #PrepareForTest(KeyVaultServiceImpl.class) works.

Getting nullPointerException because Mockito variable is set to null even though I have mocked it

Here is the class and the method I am trying to unit test.
public abstract class ShowService {
#Resource(name = "blogCoreSolrClient")
private SolrClient blogCoreSolrClient;
protected Show findShow(ClientRegion clientRegion, TargetLocale locale, Integer showId) {
SolrQuery query = new SolrQuery();
query.setQuery("type:" + SolrType.show)
.addFilterQuery(getRegionQuery(clientRegion))
.addFilterQuery(getLanguageFallbackQuery(locale))
.addFilterQuery("show_id:" + showId)
.setRows(1);
QueryResponse response = blogCoreSolrClient.query(query);
List<Show> shows = response.getBeans(Show.class);
if (shows != null && shows.size() > 0) {
return shows.get(0);
}
return null;
}
public static class SyndicatedShow {
#Field("show_id")
public Integer showId;
#Field("path_value")
public String pathValue;
}
}
Here's my Unit test written using Mockito
public class ShowServiceTest {
public final QueryResponse queryResponse = Mockito.mock(QueryResponse.class);
public final SolrClient blogCoreSolrClient = Mockito.mock(SolrClient.class);
public final SolrQuery SolrQuery = Mockito.mock(SolrQuery.class);
private SolrDocumentList solrDocuments = Mockito.mock(SolrDocumentList.class);
private ShowService showService = Mockito.mock(ShowService.class);
#Test
public void findShowTest() {
Mockito.when(blogCoreSolrClient.query(solrQueryCaptor.capture())).thenReturn(queryResponse);
Mockito.when(queryResponse.getResults()).thenReturn(solrDocuments);
Mockito.when(blogCoreSolrClient.query(any())).thenReturn(queryResponse);
ShowService.Show showsResult = ShowService.findShow(ClientRegion.US, TargetLocale.EN_US, 1234);
assertThat(showsResult.pathValue).isEqualTo("shows/test/");
}
}
I am getting Null at blogCoreSolrClient when the code passes to findShow().
Because of that I am getting NullPointerException.
Any suggestions, where I might be going wrong. TIA
There are different problems :
You're mocking the class under test
private ShowService showService = Mockito.mock(ShowService.class);
You don't tell to your ShowService to use the blogCoreSolrClient mock, then the blogCoreSolrClient mock instance is created, but never used.
As you are using IOC to inject the SolrClient (#Resource annotation), you need to inject the mock in your ShowService instance during the test.
There are different solutions, but the more convenient in your case would be to use the Mockito Extension to perform the injection.
Something like :
#ExtendWith(MockitoExtension.class)
public class ShowServiceTest {
#Mock
private SolrClient blogCoreSolrClient;
... // Other mocks
#InjectMocks
private ShowService showService;
#Test
public void findShowTest() {
Mockito.when(blogCoreSolrClient.query(any())).thenReturn(queryResponse);
... // Complete test
}
}
The problem would be that your class under test is an abstract class.
I don't see any abstract method. So, if you really need the class under test to be abstract, then you'll face a technical difficulty with #InjectMocks.
Here are some solutions :
https://tedvinke.wordpress.com/2020/06/22/mockito-cannot-instantiate-injectmocks-field-the-type-is-an-abstract-class/

Argument passed to verify is not a Mock

I'm new to writing tests in Java and trying to learn Mockito.
This is part of the class I want to test.
public class ExamRepository implements IExamRepository {
private static final Logger LOGGER = LogManager.getLogger(ExamRepository.class);
private IStudentRepository studentRepository = new StudentRepository();
private EntityManager entityManager;
public ExamRepository() {
entityManager = EntityController.getEntityManager();
}
public ExamRepository(EntityManager entityManager){
this.entityManager = entityManager;
}
// Get all exam skeletons from the DB
#Override
public List<ExamSkeleton> getAllSkeletons() {
try {
TypedQuery<ExamSkeleton> query = entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s", ExamSkeleton.class);
return query.getResultList();
} catch (IllegalArgumentException exception) {
LOGGER.error(exception);
}
return Collections.emptyList();
}
}
This is the actual test I wrote, I have a feeling the error:
Argument passed to verify() is of type ExamRepository and is not a mock!
Is occurring because of this line:
examRepository = new ExamRepository(entityManager);
However I am uncertain as to how to rewrite it. I am also not sure what else I should be testing for except for the fact that it ran once.
public class ExamRepositoryTest {
#InjectMocks
private ExamRepository examRepository;
#Mock
private EntityManager entityManager;
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Test
public void canGetAllSkeletons(){
examRepository = new ExamRepository(entityManager);
List<ExamSkeleton> examSkeletons = new ArrayList<>();
examSkeletons.add(new ExamSkeleton());
examSkeletons.add(new ExamSkeleton());
TypedQuery query = mock(TypedQuery.class);
when(entityManager.createQuery(anyString(), Matchers.anyObject())).thenReturn(query);
when(query.getResultList()).thenReturn(examSkeletons);
verify(examRepository, times(1)).getAllSkeletons();
}
}
Hope you guys can put me on the correct track!
Use it like this:
#Test
public void canGetAllSkeletons(){
examRepository = new ExamRepository(entityManager);
List<ExamSkeleton> expected = new ArrayList<>();
expected.add(new ExamSkeleton());
expected.add(new ExamSkeleton());
TypedQuery query = mock(TypedQuery.class);
when(entityManager.createQuery(anyString(), anyObject())).thenReturn(query);
when(query.getResultList()).thenReturn(expected);
List<ExamSkeleton> result = examRepository.getAllSkeletons();
assertEquals(expected, result);
}
Your idea was almost correct, but instead of verifying a call, you just need to do the actual invocation of examRepository.getAllSkeletons() in your test and check the result against your expected return value for the TypedQuery.
And because you can only get the correct result list if your previous mocking steps worked, you don't need to verify any invocation on the EntityManager mock.

Mockito verify unit test - Wanted but not invoked. Actually, there were zero interactions with this mock

At first I want to sorry for my english.
I started to make some unit tests (i've never done this before, i'm a new guy in programming).
I have to test simple adding product to database (DynamoDB) method using mockito.verify but I have
"Wanted but not invoked. Actually, there were zero interactions with this mock."
Error and I don't know what to do.
This is my method code (in KitchenService class):
public Product addProduct(Product content) {
ObjectMapper objectMapper = new ObjectMapper();
String mediaJSON = null;
String authorJSON = null;
String productKindsJSON = null;
try {
mediaJSON = objectMapper.writeValueAsString(content.getMedia());
authorJSON = objectMapper.writeValueAsString(content.getAuthor());
productKindsJSON = objectMapper.writeValueAsString(content.getProductKinds());
} catch (JsonProcessingException e) {
logger.log(e.getMessage());
}
Item item = new Item()
.withPrimaryKey("id", UUID.randomUUID().toString())
.with("name", content.getName())
.with("calories", content.getCalories())
.with("fat", content.getFat())
.with("carbo", content.getCarbo())
.with("protein", content.getProtein())
.with("productKinds", productKindsJSON)
.with("author", authorJSON)
.with("media", mediaJSON)
.with("approved", content.getApproved());
Item save = databaseController.saveProduct(PRODUCT_TABLE, item);
logger.log(save + " created");
return content;
}
And this is test code:
#Test
public void addProduct() throws Exception {
KitchenService instance = mock(KitchenService.class);
Product expectedProduct = new Product();
expectedProduct.setName("kaszanka");
expectedProduct.setCalories(1000);
expectedProduct.setFat(40.00);
expectedProduct.setCarbo(20.00);
expectedProduct.setProtein(40.00);
expectedProduct.setProductKinds(Collections.singletonList(ProductKind.MEAT));
expectedProduct.setApproved(false);
Author expectedAuthor = new Author();
expectedAuthor.setId("testID");
expectedAuthor.setName("Endrju Golota");
expectedProduct.setAuthor(expectedAuthor);
Media expectedMedia = new Media();
expectedMedia.setMediaType(MediaType.IMAGE);
expectedMedia.setName("dupajasia");
expectedMedia.setUrl("http://blabla.pl");
expectedProduct.setMedia(expectedMedia);
verify(instance, times(1)).addProduct(expectedProduct);
}
This is what I got after test:
Wanted but not invoked:
kitchenService.addProduct(
model.kitchen.Product#a0136253
);
-> at service.kitchen.KitchenServiceTest.addProduct(KitchenServiceTest.java:80)
Actually, there were zero interactions with this mock.
Can someone tell me what im doing wrong?
What you should mock and verify is the databaseController dependency:
#Test
public void addProduct() throws Exception {
KitchenService instance = new KitchenService(); // you should create the class under test
DatabaseController controllerMock = mock(DatabaseController.class); // mock the controller
instance.setController(controller); // inject the mock
...
// Act
instance.addProduct(expectedProduct);
// Assert
verify(controller).saveProduct(Mockito.eq(PRODUCT_TABLE), Mockito.any(Item.class));
}
You should verify that the database is called within the service.. checking that it was invoked with any Item object should be enough.
Mocking is a tool that you only use for dependencies of the class that is being tested.
It appears that your test does not care about the Author, Media, and Product objects,
these are just dependencies of the method you want to test;
mock them.
Organization will greatly help your test;
do something like this:
public class TestKitchenService
{
private static String VALUE_PRODUCT_NAME = "VALUE_PRODUCT_NAME";
... use constants for other values as well. The value of the constant does not matter.
#InjectMocks
private KitchenService classToTest;
private InOrder inOrder;
#Mock
private Author mockAuthor;
#Mock
private DatabaseController mockDatabaseController;
#Mock
private Logger mockLogger;
#Mock
private Media mockMedia;
#Mock
private Product mockProduct;
#After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockAuthor);
verifyNoMoreInteractions(mockDatabaseController);
verifyNoMoreInteractions(mockLogger);
verifyNoMoreInteractions(mockMedia);
verifyNoMoreInteractions(mockProduct);
}
#Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doReturn(mockAuthor).when(mockProduct).getAuthor();
doReturn(mockMedia).when(mockProduct).getMedia();
doReturn(VALUE_PRODUCT_NAME).when(mockProduct).getName();
doReturn(Collections.singletonList(ProductKind.MEAT)).when(mockProduct).getProductKinds();
... doReturns for the other product values.
inOrder = inOrder(
mockAuthor,
mockDatabaseController,
mockLogger,
mockMedia,
mockProduct);
ReflectionTestUtils.setField(
classToTest,
"databaseController",
mockDatabaseController);
ReflectionTestUtils.setField(
classToTest,
"logger",
mockLogger);
}
#Test
public void addProduct_success()
{
final Product actualResult;
actualResult = classToTest.addProduct(mockProduct);
assertEquals(
mockProduct,
actualResult);
inOrder.verify(mockProduct).getMedia();
inOrder.verify(mockProduct).getAuthor();
inOrder.verify(mockProduct).getProductKinds();
inOrder.verify(mockProduct).getName();
... inOrder.verify for the other product values.
inOrder.verify(mockDatabaseController).saveProduct(
eq(PRODUCT_TABLE),
any(Item.class));
}
}
The only things that should be mocked -- if anything -- are the ObjectMapper and databaseController. One only mocks collaborator objects, and almost never the system/class under test (very rare cases exist for "spying" on the SUT). And depending on what the ObjectMapper is and how transparent it's operation is, you may not really want to even mock that. Furthermore, as your implementation code is written instantiating the ObjectMapper by directly calling a constructor, you can't even mock it.
While I love the use of Mockito and mock objects, sometimes it is worthwhile to simply test with as many real objects as possible. This is especially true when your collaborators are simple, straightforward, have no side effects, and don't require complex initialization or setup. Only use mocks when it simplifies the test setup or verification.

Play Framework 2.1.1 doesn't invoke mockito.

I'm trying to mock Model.finder to test my service, but it seems mockito isn't being injected for some reason and I cannot figure out why. Please help.
public static Result deals() {
List<Product> finder = new Model.Finder<Long, Product>(Long.class, Product.class).all();
JsonNode jsonNode = Json.toJson(finder);
return ok(Json.stringify(jsonNode));
}
And here's my test
#Mock
private Model.Finder finder;
#Before
public void setUp() throws Exception {
initMocks(this);
start(fakeApplication());
Product product = new Product();
product.id = 1L;
product.title = "Milk";
List<Product> products = Arrays.asList(product);
when(finder.all()).thenReturn(products);
}
#Test
public void shouldGetDeals() {
Result result = routeAndCall(fakeRequest(GET, "/products"));
assertThat(status(result), is(200));
String deals = contentAsString(result);
assertThat(deals, containsString("Milk"));
}
So, the result is that Model.Finder returns 0 because the mock is not invoked. I'm not sure if this is how you mock in Play 2.1?
Your deals() method is making a new Finder, rather than using the mock one that you created. You're going to need to refactor your code a little, to make it more testable. You may want to read https://code.google.com/p/mockito/wiki/MockingObjectCreation for a couple of ideas on how you could do this.

Categories

Resources