Am I testing this correctly? - java

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).

Related

Mock test always stops at isEmpty checking and throw an exception

I've written test for update method in service layer and it always throws an exception "User with the this id is not found" and didn't work.
I've tried to add optional but it didn't work too.
Could you give me a little piece of advice please? What should I fix in my test?
My testing method looks like:
#Override
public UserDTO updateUser(String id, UserDTO updatedUser) {
Optional<UserEntity> databaseUser = userRepository.findById(Integer.valueOf(updatedUser.getUserName()));
if (databaseUser.isEmpty()) {
throw new UserNotFoundException("User with the this id is not found");
}
UserEntity entity = mapToUserEntity(updatedUser);
return map(userRepository.save(entity));
}
My test looks like:
#Test
void updateUserTest(){
final int id = 1;
final long roleId = 2L;
UserDTO userDto = new UserDTO();
userDto.setUserName(String.valueOf(12));
userDto.setId(String.valueOf(id));
userDto.setName(new UserDTO.Name("surname", "firstname", "patronymic"));
userDto.setActive(true);
userDto.setEmails(List.of(new UserDTO.Email("email", "external")));
userDto.setRoles(List.of("2"));
userDto.setLastAccessDate(LocalDateTime.of(2022, 10, 25, 4, 20));
userDto.setUnit(null);
when(roleRepository.findById(any())).thenReturn(Optional.of(new UserDTO().setId(roleId)));
UserEntity userEntity = userService.mapToUserEntity(userDto);
when(userRepository.save(any())).thenReturn(userEntity.setId(id));
userService.updateUser(String.valueOf(id), userDto);
var actualUser = userService.updateUser(String.valueOf(id), userDto);
userDto.setUserName(String.valueOf(id));
assertEquals(actualUser, userDto);
}
The updateUser method calls userRepository.findById(Integer.valueOf(updatedUser.getUserName())).
The test does not stub this method, so the userRepository mock object will use the default answer behaviour to return a value. The example code does not show how the userRepository mock is created, but if the default answer is not customised, it will use RETURNS_DEFAULTS and return an empty Optional by default for methods returning Optional.
To make it return userDto instead, you should stub the method before calling userService.updateUser in the test:
when(userRepository.findById(12)).thenReturn(Optional.of(userEntity));
when(userRepository.save(any())).thenReturn(userEntity.setId(id));
userService.updateUser(String.valueOf(id), userDto);

How to workaround orElseThrow mock

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
}

Test method fails when #Transactional

I am writing my end-to-end tests for an application. One of those tests will verify that when some required input is missing, it will not persist the data to the database. All is running fine, untill I decided to annotate my test class with #Transactional, since I don't need to persist the results when the tests are finished. The moment I add the #Transactionalannotation, the application is suddenly ok with a non-null field to be null.
How the database is set up:
create table MY_TABLE
(
MY_FIELD VARCHAR(20) NOT NULL
)
The corresponding model:
#Entity
#Table(name = "MY_TABLE")
public class MyObject {
#Column(name = "MY_FIELD", nullable = false)
private String MyField = null;
}
Saving goes via the service layer:
#Service
public class MyAppServiceImpl implements MyAppService {
#Inject
private MyObjectRepository myObjectRepository; //this interface extends CrudRepository<MyObject , Long>
#Override
#Transactional
public MyObject save(MyObject myObject) {
return myObjectRepository.save(myObject);
}
}
In the resource layer, the value of myField is set to NULL, so at the moment the data is persisted, I should receive an ORA-error
#Controller
public class MyAppResourceImpl implements MyAppResource {
#Inject
private MyAppService myAppService;
public ResponseEntity doPost(#RequestBody String xml) {
MyObject myobject = new MyObject();
myObject.setMyField(null);
try {
myAppService.save(myObject);
return new ResponseEntity(null, HttpStatus.CREATED);
} catch (SomeExceptions e) {
return new ResponseEntity("Could not save", HttpStatus.BAD_REQUEST);
}
}
}
Finally, the test:
#RunWith(SpringRunner.class)
#SpringBootTest
//#Transactional
public class MyAppResourceImplEndToEndTest {
#Inject
private MyAppResourceImpl myAppResource;
#Test
public void testWithFieldNull() throws Exception {
ResponseEntity response = myAppResource.doPost("some-xml");
Assertions.assertThat(response.GetStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
}
}
This test works, as expected, but other tests persist data into the database. This is not the behavior I wanted, so I annotated the test class with #Transactional. Now no tests persist their results to the database, but this specific test method fails because
Expected :400
Actual :201
Why does the #Transactional annotation suddenly cause NULL values to be persisted in NOT NULL columns?

Mockito Java: How to test a method which calls other class to retrieve value

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();
...

Junit/Mockito unit with Spring bean dependencies

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());
}
}

Categories

Resources