I need to test following method inside ProductManager class which is a Spring bean. productService a bean and injected into ProductManager class. I try to write a Junit test using Mockito, but it kept to invoke real productService.getProductIdList(email) method instead of mock. ProductService also has a #PostConstruct. Could anyone enlighten me what's wrong with my test code?
#Named(ProductManager.NAME)
public class ProductManager {
#Resource(name = ProductService.NAME)
private ProductService productService;
public static final String NAME = "productManager";
public Set<Product> getProducts(String email) {
Set<Integer> productList = productService.getProductIdList(email);
Iterator<Integer> itr = productList.iterator();
Set<Product> products = new HashSet<Product>();
Product p = null;
while (itr.hasNext()) {
p = getProduct(itr.next());
if (p != null) {
products.add(p);
}
}
return products;
}
public Product getProduct(Integer ProductId) {
Product p = productService.getProduct(productId);
return p;
}
}
So far, I have following Junit test code.
#Test
public void getProductByEmail(){
String email = "testfakeuser#gmail.com";
ProductService mockProductService = mock(ProductServiceImpl.class);
Integer productId1 = 10000;
Integer productId2 = 10002;
Product p1 = mock(Product.class);
Product p2 = mock(Product.class);
when(p1.getProductId()).thenReturn(productId1);
when(p2.getProductId()).thenReturn(productId2);
Set<Integer> productIdSet = (Set<Integer>)mock(Set.class);
productIdSet.add(productId1);
productIdSet.add(productId2);
Iterator<Integer> productIdIterator = (Iterator<Integer>)mock(Iterator.class);
when(productIdSet.iterator()).thenReturn(productIdIterator);
when(mockProductService.getProductIdList(email)).thenReturn(productIdSet);
when(productIdIterator.hasNext()).thenReturn(true, true, false);
when(productIdIterator.next()).thenReturn(productId1).thenReturn(productId2);
when(productManager.getProduct(productId1)).thenReturn(p1);
when(productManager.getProduct(productId2)).thenReturn(p2);
Set<Product> products = productManager.getProducts(email);
assertEquals(2, products.size());
}
I don't see any where where you set your mocked ProductService in to the ProductManager object.
You've created a intricate set of related objects, but not asked the ProductManager to use it.
I am answering to my own question. I resolved the issue using Spring ReflectionTestUtils. It can set mock dependencies. I referenced http://romiawasthy.blogspot.com/2012/03/autowired-mockobjects-for-junits.html. I tried second solution, but I couldn't make it work.
In this Test, the productManager is not mocked. It is a real spring bean. Another way of doing is don't use Spring context at all, just use Mockito RunWith(MockitoJUnitRunner.class). In this case all beans are mocked including the productManager. I did some research yesterday and using MockitoJUnitRunner.class is preferable since it can void reapeating code and you have full control of your test environment. Please look at this article for using MockitoJUnitRunner.class http://www.jayway.com/2012/02/25/mockito-and-dependency-injection/. It is clear and simple.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:context-test.xml")
public class ProductManagerTest {
#Inject
private ProductManager productManager;
#Test
public void getProductByEmail(){
String email = "testfakeuser#gmail.com";
ProductService mockProductService = mock(ProductServiceImpl.class);
Integer productId1 = 10000;
Integer productId2 = 10002;
Product p1 = mock(Product.class);
Product p2 = mock(Product.class);
p1.setProductId(productId1);
p2.setProductId(productId2);
Set<Integer> productIdSet = (Set<Integer>)mock(Set.class);
productIdSet.add(productId1);
productIdSet.add(productId2);
Iterator<Integer> productIdIterator = (Iterator<Integer>)mock(Iterator.class);
when(productIdSet.iterator()).thenReturn(productIdIterator);
when(mockProductService.getProductIdList(email)).thenReturn(productIdSet);
ReflectionTestUtils.setField(productManager, "mockProductService",
mockProductService);
when(productIdIterator.hasNext()).thenReturn(true, true, false);
when(productIdIterator.next()).thenReturn(productId1).thenReturn(productId2);
when(productManager.getProduct(productId1)).thenReturn(p1);
when(productManager.getProduct(productId2)).thenReturn(p2);
Set<Product> products = productManager.getProducts(email);
assertEquals(2, products.size());
}
}
Related
I have a Java Springboot web API project that uses Azure table storage as the data store. I'd like to create a unit test to make sure that the repository is properly converting an Azure TableEntity into a custom Tag object in the repository. However, I am not able to figure-out a way to mock the Azure PagedIterable<TableEntity> that is returned by the Azure TableClient.listEntities() function.
At the core of my repository class is the following function that returns a filtered list of table entities:
private PagedIterable<TableEntity> getFilteredTableRows(String filter, String tableName) {
ListEntitiesOptions options = new ListEntitiesOptions().setFilter(filter);
TableClient tableClient = tableServiceClient.getTableClient(tableName);
PagedIterable<TableEntity> pagedIterable = tableClient.listEntities(options, null, null);
return pagedIterable;
}
How do I ensure the TableClient is mocked and returns a valid PagedIterable<TableEntity>?
Below is sample JUnit test class that uses Mockito to mock the Azure PagedIterable<T> object and return a single TableEntity that is mapped to a custom Tag model in the repository code.
The test setup requires four mocks:
A mock Iterator
A mock PagedIterable
A mock TableServiceClient
A mock TableClient
If there is an easier way to accomplish the same thing, I'm open to suggestions.
#ExtendWith(MockitoExtension.class)
#MockitoSettings(strictness = Strictness.LENIENT)
public class DocTagRepositoryTest {
#InjectMocks
#Spy
DocTagRepository docTagRepository;
#Mock
TableServiceClient tableServiceClient;
#Mock
TableClient tableClient;
private static TableEntity testTableEntity;
private static Tag testTagObject;
#SneakyThrows
#BeforeAll
public static void setup() {
loadTableObjects();
}
#Test
public void testGetTagList() {
// Given: A request to get tags from Azure table storage...
Iterator mockIterator = mock(Iterator.class);
when(mockIterator.hasNext()).thenReturn(true, false);
when(mockIterator.next()).thenReturn(testTableEntity);
PagedIterable mockPagedTableEntities = mock(PagedIterable.class);
when(mockPagedTableEntities.iterator()).thenReturn(mockIterator);
when(tableServiceClient.getTableClient(Mockito.anyString())).thenReturn(tableClient);
when(tableClient.listEntities(any(), any(), any())).thenReturn(mockPagedTableEntities);
List<Tag> expected = new ArrayList<>();
expected.add(testTagObject);
// When: A call is made to the repository's getActiveTags() function...
List<Tag> actual = docTagRepository.getActiveTags();
// Then: Return an array of tag objects.
assertArrayEquals(expected.toArray(), actual.toArray());
}
private static void loadTableObjects() {
OffsetDateTime now = OffsetDateTime.now();
String testUser = "buh0000";
String rowKey = "test";
String partitionKey = "v1";
String activeStatus = "A";
Map<String, Object> properties = new HashMap<>();
properties.put("createdDate", now);
properties.put("createdBy", testUser);
properties.put("modifiedDate", now);
properties.put("lastModifiedBy", testUser);
properties.put("status", activeStatus);
testTableEntity = new TableEntity(partitionKey, rowKey);
testTableEntity.setProperties(properties);
testTagObject = new Tag(partitionKey, rowKey, now, testUser, now, testUser, activeStatus);
}
}
I have a ProductService that through ProductRepository queries a database. In there I have an update method and a find method. The update method is updateProductInDatabase(String id, Product updateInfo). The updateProductInDatabase calls a method findProductInDatabaseById(String id) which return Product or throws a ResourceNotFoundException.
My code:
public void updateProductInDatabase(String id, Product updateInfo) {
Product product = findProductInDatabaseById(id);
if (correctFormat(updateInfo.getVersion()) {
product.setVersion(updateInfo.getVersion());
repository.save(updateInfo);
//restOfTheCode
} else {
// Throws invalid input exception
}
}
private Products findProductInDatabaseById(String id) {
Optional<Product> productOptional =
repository.getAllProducts().stream.findFirst(); // return a list, but I only need the first
return productOptional.orElseThrow(...) // Throws resource not found exception
}
I want to write unit test for this code that expects the invalid input exception, but the test fails with
unexpected exception: expected InvalidInputException, but found
ResourceNotFoundException
This happens because productOptional is always an empty optional.
Can someone help in providing a workaround to mocking productOptional ?
Edit: adding my test
#Test(expected = InvalidInputException.class)
public void testUpdateProductVersionInDatabaseWhenVersionIsIncorrectFormat()
throws ApiException {
Product product = new Product();
product.setVersion("error-version");
when(repository.getAllProducts())
.thenReturn(Collections.singletonList(new Product()));
productService.updateProductInDatabase("product-id-1", product);
}
Can you do something like
List<Product> products = new LinkedList<>();
products.add(new Product()); // add product
Mockito.when(repository.getAllProducts()).thenReturn(products);
It means that the state of the DB isn't sutable for the test since you expect some data meanwhie there are no products. Insert some product before the test.
If you really don't care of the data, then yes, it could be mocked:
class ProductServiceTestClass {
#Mock
ProductRepository repository;
#InjectMocks
ProductService productService;
#Test(expected = InvalidInputException.class)
public void testUpdateProductVersionInDatabaseWhenVersionIsIncorrectFormat()
throws ApiException {
Product product = new Product();
product.setVersion("error-version");
when(repository.getAllProducts())
.thenReturn(Collections.singletonList(new Product()));
productService.updateProductInDatabase("product-id-1", product);
}
}
Note that it won't work if ProductRepository injected by #Autowired. In this case the real Spring bean will be used instead. Change its injection to constructor or setter way.
You should refactor your code so the validation method can be unit tested
Extract method correctFormat(updateInfo.getVersion() to a utility class like :
VersionValidator.isCorrectFormat(String version)
you will be able to unit test it :
#Test
public void should_return_false_on_incorrect_format_version()
throws ApiException {
String uncorrectFormat = "LTS.x.v15dssdf";
boolean isCorrect = VersionValidator.isCorrectFormat(uncorrectFormat);
assertThat(isCorrect).isFalse()
}
anyway, if you want to keep with an integration test, your method repository.getAllProducts() should at least return a product.
public void updateProductInDatabase(String id, Product updateInfo) {
if (!correctFormat(updateInfo.getVersion()) {
//throw
}
Product product = findProductInDatabaseById(id);
product.setVersion(updateInfo.getVersion());
repository.save(updateInfo);
//restOfTheCode
}
struggling long time with that issue and I have the weird feeling it has something to do on how I am setting my #Transactional annotation.
So what do I want to do?
I am preparing some data and save them with the available repositories in the database.
This can be found here in my FormTest class in the prepareExampleApplication method
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class FormTest {
#Autowired
private ApplicationRepository applicationRepository;
#Autowired
private AppActionRepository appActionRepository;
#Autowired
private RoleRepository roleRepository;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private FormRepository formRepository;
#Autowired
private FormElementRepository formElementRepository;
#Autowired
private SectionRepository sectionRepository;
private Application application;
private InputField refInputField;
private Select refSelectBox;
#Before
public void prepareExampleApplication() {
Form form = formRepository.save(ModelFactory.getForm("Project"));
Application application = ModelFactory.getApplication("Example", form);
this.application = applicationRepository.save(application);
Role role = new Role();
role.setRoleName("ADMIN");
role.setApp(application);
role = roleRepository.save(role);
Section section = ModelFactory.getSection(form, null, null);
section = formElementRepository.save(section);
InputField inputField = ModelFactory.getInputField(form, section, section);
refInputField = formElementRepository.save(inputField);
//once again. Just for my own eyes to see if it is there
Iterable<Form> all = formRepository.findAll();
// lot more stuff
}
#Test
#Transactional
public void testUserInput() {
// first create a new container to give inouts
Long id = this.application.getEntity().getId();
// for the sake of debugging I am using the formRepo to really SEARCH for the persisted form AND IT IS THERE!!!
Form byId = formRepository.findById(id).orElseThrow(NotFoundException::new);
URI uri = this.restTemplate.postForLocation("/api/form/" + id + "/forminstance", null);
long containerId = TestHelper.extractId(uri);
}
}
The next thing I am doing having this data is to use the restTemplate and dispatch a post request to a REST service. You can find the POST call in the test method. For debugging reasons - and to see that the repo is working - I am REALLY using the repository to get the id of the form instead of using the class field that had been filled with the preparation method. AND the form will be returned!!
Within my rest service I am using the formRepository once again, looking for the entity I have found before in the test class. But THIS TIME the repository does not return anything. Only null. I have tried SO MANY different things with setting #Transactional on different locations, but whatever I do the formRepository within the REST service does only give me back 0 entites and a null. Here is the REST service
#Service
#Api("FormService")
#Path("/form")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class FormService {
#Autowired
private FormInstanceRepository formInstanceRepository;
#Autowired
private FormRepository formRepository;
#Autowired
private FormElementRepository formElementRepository;
#Autowired
private UserInputRepository userInputRepository;
#POST
#Path("{id}/forminstance")
#Transactional
public Response createFormInstance(#Context UriInfo info, #PathParam("id") long formId) {
// returns ALWAYS 0 elements
Iterable<Form> all = formRepository.findAll();
// returns always null
Form form = formRepository.findById(formId).orElse(null);
FormInstance formInstance = new FormInstance();
formInstance.setForm(form);
FormInstance save = formInstanceRepository.save(formInstance);
UriBuilder builder = info.getAbsolutePathBuilder();
builder.path(Long.toString(save.getId()));
return Response.created(builder.build()).build();
}
IF you know the answer I am really interested in the explanation to understand my error. I am using an in-memory H2 db for the tests.
Adding the Form entity and FormRepository, too
#Entity
#Data
public class Form {
#Id
#GeneratedValue
private Long id;
private String name;
}
-
public interface FormRepository extends CrudRepository<Form, Long> {
}
Thanks in advance for your help!!
I have the following method on a service class:
#Service
public class Service {
(...)
public Page<ChannelAccount> getByCustomerAndChannelType(Pageable pageable, Customer customer, ChannelType channelType) {
return channelAccountRepository.findByCustomerAndChannelType(pageable, customer, channelType);
}
}
This returns the expected result. Now I trying to build the unit test for it. So far I got this:
#RunWith(MockitoJUnitRunner.class)
public class ChannelAccountServiceTest {
#InjectMocks
private ChannelAccountService channelAccountService;
#Mock
private ChannelAccountRepository channelAccountRepository;
(...)
#Test
public void testGetByCustomerAndChannelTypePageable() {
Page<ChannelAccount> pageResult = new PageImpl<>(channelAccountService.getAllChannelAccounts());
Mockito.when(channelAccountRepository.findByCustomerAndChannelType(pageable, customer, ChannelType.FACEBOOK)).thenReturn(pageResult);
Page<ChannelAccount> channelAccountPage = channelAccountRepository.findByCustomerAndChannelType(pageable, customer, ChannelType.FACEBOOK);
assertEquals(pageResult, channelAccountPage);
}
Somehow this doesn't feels right. What am I missing here?
Not sure why you are calling this method as it has nothing to do with the case itself:
Page<ChannelAccount> pageResult = new PageImpl<>(channelAccountService.getAllChannelAccounts());
I would do the following in the test:
Pageable pageableStub = Mockito.mock(Pageable.class);
Page pageStub = Mockito.mock(Page.class);
Mockito.when(channelAccountRepository
.findByCustomerAndChannelType(pageableStub, customer, ChannelType.FACEBOOK))
.thenReturn(pageStub);
Page<ChannelAccount> channelAccountPage = channelAccountService
.findByCustomerAndChannelType(pageableStub, customer, ChannelType.FACEBOOK);
assertTrue(pageResult == channelAccountPage);
I would check whether the objects are the same instances instead of equals (even more strict).
I'm trying to test a method intialize transaction where the method creates a unique transaction Id. This method uses reference of other class to retrieve the properties.
After mocking the reference classes am still getting null pointer exception. when i try to test. Below is my code.
Note: JMockito
Any help appreciated
public ResponseDto initializeTransaction(RequestDTO request){
try {
String transactionId =getTransactionId(request);
ResponseDTO result = new ResponseDTO();
result.setTransactionId(transactionId);
return result;
}
}
public String getTransactionId(CreditCardGwtInitializeRequestDTO request){
StringBuffer transactionId = new StringBuffer();
String customerId = customerIdentifier.getCustomer();
UserDto userDto = user.getUserDetails(request.getKey());
String userWorkStationId =userDto.getWorkStationId();
transactionId.append(String.valueOf(System.currentTimeMillis()) + "-");
transactionId.append(userDto.getObjId()+ "-");
transactionId.append(transactionIdEncode.encode(customerId));
transactionId.append("-");
transactionId.append(transactionIdEncode.encode(userWorkStationId));
return transactionId.toString();
}
Test class
public class CreditCardGwtInitializeServiceImplTest {
private CreditCardGwtInitializeServiceImpl test;
#Mock
private CustomerIdentifier customerIdentifier;
#Mock
private UserDto userDto;
#Mock
private UserDetails user;
private CreditCardGwtInitializeRequestDTO request;
#Mock
TransactionIdCharacterEncoding transactionId;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void when_profilename_notNull_retrieveByName() throws Exception {
//test.setUser(user);
CreditCardGwtInitializeResponseDTO expected = new CreditCardGwtInitializeResponseDTO();
expected.setGatewayPassKey("");
String profileName="theName";
String connectionKey ="123456";
String custId ="custId";
request.setGatewayProfile(profileName);
request.setConnectionKey(connectionKey);
//userDto.setWorkStationId("12345");
//userDto.setObjId(12345L);
when(customerIdentifier.getCustomer()).thenReturn(custId);
when(user.getUserDetails(anyString())).thenReturn(userDto);
when(userDto.getWorkStationId()).thenReturn("RTYTYU");
when(userDto.getObjId()).thenReturn(1232324L);
when(transactionId.encode(anyString())).thenReturn("01010101");
CreditCardGwtInitializeResponseDTO response = test.initializeTransaction(request);
assertEquals(expected,response );
verifyZeroInteractions(gatewayProfileRetrievalService);
}
By adding thes lines solved my problem
MockitoAnnotations.initMocks(this);
test = new CreditCardGwtInitializeServiceImpl();
test.setUser(user);
test.setCustomerIdentifier(customerIdentifier);
test.setTransactionIdEncode(transactionId);
Here is an alternative solution that uses Mockito magic instead of manual initialization (replaces the setup method).
First override the default JUnit Runner class by the one provided by mockito (this has the same effect as MockitoAnnotations.initMocks(this)):
#RunWith(MockitoJUnitRunner.class)
public class CreditCardGwtInitializeServiceImplTest {
...
then instantiate your object right after your declare it and let mockito take care of the injection (this has the same effect as the 5 lines of your solution) :
#InjectMocks
private CreditCardGwtInitializeServiceImpl test = new CreditCardGwtInitializeServiceImpl();
...