//Original method:
#Autowired
private ConversionServiceValidator validator;
public CRSConversionResult convertCRS(ConvertCrsVo convertCrsVo) throws Exception {
if (validator.isSameSourceAndTarget(convertCrsVo))
throw new ValidationException(Constants.BADREQUEST);
if (convertCrsVo.getPreferredTransforms() != null) {
List<TransformVo> preferredTransformList = new ArrayList<>();
for (TransformVo transformVo : convertCrsVo.getPreferredTransforms()) {
preferredTransformList.add(getPerfByCode(transformVo));
}
convertCrsVo.setPreferredTransforms(preferredTransformList);
}
convertCrsVo.setSourceCRS(getCrsVoByCode(convertCrsVo.getSourceCRS()));
convertCrsVo.setTargetCRS(getCrsVoByCode(convertCrsVo.getTargetCRS()));
convertCrsVo = validator.replaceCoordinates(convertCrsVo);
logger.info("ShellGeodeticService::convertCRS::Request to GeoCalService convertpoints::" + mapper.writeValueAsString(convertCrsVo));
ConvertPointsResponse response = geoCalService.convertCRS(convertCrsVo);
CRSConversionResult result = new CRSConversionResult();
result.setCriteriaMessage(response.getCriteriaMessage());
result.setResultPoints(response.getResultPoints());
result.setTransformName(response.getTransformName());
result.setTransformDescription(response.getTransformDescription());
// added schema as per pbi 195298
List<ConvertedTransformsResult> transformsResults = new ArrayList<>();
if (response.getTransforms() != null || !response.getTransforms().isEmpty())
response.getTransforms().stream().forEach(
t -> transformsResults.add(new ConvertedTransformsResult().getConvertedTransformsResult(t)));
result.setTransforms(transformsResults);
String logmessage=generateLogMessage(result,convertCrsVo);
logger.info(logmessage);
validator.isResponseValid(result);
return result;
}
//The testcase for the above method
#Test
public void testconvertCRSJob() throws Exception{
ConvertCrsVo convertCrsVo = TestDataFactory.getConvertCrsVo();
CRSConversionResult crsConversionResult = TestDataFactory.getCRSConversionResult();
ConversionServiceValidator conversionServiceValidatorMock = mock(ConversionServiceValidator.class);
Mockito.when(geoCalService.convertCRS(Mockito.any()))
.thenReturn(TestDataFactory.getConvertPointsResponse(convertCrsVo));
Mockito.when(validator.replaceCoordinates(convertCrsVo))
.thenReturn(TestDataFactory.getConvertCrsVo());
Mockito.when(geoCalService.search(Mockito.any(SearchFilter.class)))
.thenReturn(TestDataFactory.getSearchResultResponseForCRS());
Mockito.when(shellGeodeticService.convertCRS(convertCrsVo))
.thenReturn(TestDataFactory.getCRSConversionResult());
shellGeodeticService.convertCRSJob();
}
The error that i am getting is as below:
org.mockito.exceptions.misusing.CannotStubVoidMethodWithReturnValue:
'isResponseValid' is a void method and it cannot be stubbed with a return value!
Voids are usually stubbed with Throwables:
doThrow(exception).when(mock).someVoidMethod();
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. The method you are trying to stub is overloaded. Make sure you are calling the right overloaded version.
2. Somewhere in your test you are stubbing final methods. Sorry, Mockito does not verify/stub final methods.
3. 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.
4. Mocking methods declared on non-public parent classes is not supported.
at com.shell.geodetic.GeodeticConvertionApiAppTests.testconvertCRSJob(GeodeticConvertionApiAppTests.java:1783)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
Can someone help me on how to stub the void method "isResponseValid" ? I tried around 100 combinations that i saw in SOF and nothing worked. Thanks for the help in advance.
*Edit
Class ConversionServiceValidator {
public void isResponseValid(CRSConversionResult response) throws InvalidDataException {
if (response.getResultPoints().isEmpty() || response.getResultPoints() == null) {
throw new ValidationException("Request body has incorrect format");
} else {
for (Point point : response.getResultPoints()) {
if (point.getX().trim().equals("0") || point.getY().trim().equals("0")) {
throw new InvalidDataException(400, "Bad Request", "WARNING: Not all points could be converted",
response);
}
}
}
It is a mock #InjectMocks ShellGeodeticService shellGeodeticService;
shellGeodeticService is not a mock. #InjectMocks is used for the class under test, where the mocks are injected into.
That implies you can not use
Mockito.when(shellGeodeticService.convertCRS(convertCrsVo))
.thenReturn(TestDataFactory.getCRSConversionResult());
in your test as only mocks(or spys) can be used within Mockito.when.
Actually im trying to run test case for shellGeodeticService.convertCRS() and since it calls isResponseValid method internally , i have to mock that also right?
No, that is incorrect. If validator is a mock every method invocation will do nothing by default. So, unless you want to throw an exception, you do not need to define anything.
As your question lacks some details, I assume a complete version of your test could be similiar to this:
#InjectMocks
ShellGeodeticService shellGeodeticService;
#Mock
ConversionServiceValidator validator;
#Mock
... geoCalService; // some unknown class
#Test
public void testconvertCRSJob() throws Exception{
ConvertCrsVo convertCrsVo = TestDataFactory.getConvertCrsVo();
// Note sure whether this is correct by your logic as there is no `replacement` happening.
Mockito.when(validator.replaceCoordinates(convertCrsVo)).thenReturn(convertCrsVo);
Mockito.when(geoCalService.convertCRS(Mockito.any())).thenReturn(TestDataFactory.getConvertPointsResponse(convertCrsVo));
CRSConversionResult result = shellGeodeticService.convertCRS();
// do some assertions on the result
}
As validator is a mock:
validator.isSameSourceAndTarget(convertCrsVo) returns false be default
validator.isResponseValid( ... ) does nothing by default
As you did not add the methods getCrsVoByCode, getPerfByCode and generateLogMessage take note that if there are any further interactions with the mocked objects you'll need to add them.
(eg.: a call to geoCalService.search is not visible in your test code, so I removed the behaviour definition from the test displayed above)
Related
I am comparing two objects emsResponse and expectedEMSResponse of the same class EMSResponse in a Junit test case but the assertThat(...) test case passes despite the objects being unequal. When I try a normal equals on the two objects the output is false but the assertThat(expectedEMSResponse==eMSResponse) still passes rather than failing. Please advise.
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
#EqualsAndHashCode
public class RestconfRes {
private String response;
private int httpStatusCode;
}
#Getter
#Setter
#ToString
#EqualsAndHashCode(callSuper = true)
public class EMSResponse extends RestconfRes {
private EMSOutput output;
private EMSErrors errors;
private String message;
public EMSOutput getOutput() {
if (output == null) {
output = new EMSOutput();
}
return output;
}
}
// Junit test case
#Test
public void processJMSRequestForHappyPathTest() throws Exception {
// test case logic here
// mock logic here
EMSResponse emsResponse = processor.processJMSRequest(request);
System.out.println(" expectedEMSResponse ..." + EMSResponse.toString());
System.out.println(" processJMSRequestForHappyPathTest expectedEMSResponse ...:" + expectedEMSResponse.toString());
System.out.println("check if i am equal..."+ expectedEMSResponse.equals(emsResponse));
assertThat(expectedEMSResponse==eMSResponse);
}
Output:
expectedEMSResponse ...EMSResponse(output=EMSoutput(deviceName=null, timestamp=null, status=failure, transId=null, completionStatus=null, serviceId=null, contentProviderName=null, interfaceName=null), errors=EMSerrors(errorType=INTERNAL_FAILURE, errorTag=A41_INTERNAL_EXCEPTION, errorMessage=An internal error occurred within A41. Please contact ASG.), message=null)
emsResponse ...EMSResponse(output=EMSoutput(deviceName=device_3, timestamp=2020-07-15T16:32:31.881000, status=configuring, transId=66427-d8b5-489f-9f8f, completionStatus=in-progress, serviceId=null, contentProviderName=null, interfaceName=null), errors=null, message=null)
check if i am equal...false
Updated second sub question to this
I changed the call to:
assertTrue(expectedEMSResponse==emsResponse);
// gives a java.lang.AssertionError and
System.out.println("check if i am equal..."+ expectedEMSResponse.equals(emsResponse)); gives output
check if i am equal...false
// this test case passes Ok assertThat(emsResponse.getOutput().getTransId()).isEqualTo(expectedEMSResponse.getOutput().getTransId());
When I try printing the details of the emsResponse and expecteEMSResponse classes through toString() I get the below output:
// gives only a bean as an output
emsResponse.toString():com.myorg.myapp.interfaces.dto.emsResponse#0 bean
//gives the complete object signature
expectedemsResponse.toString():emsResponse(output=emsOutput(deviceName=device_3,
timestamp=2020-07-15T16:32:31.881000, status=configuring, transId=hello-there-489f-9f8f-abcd,
completionStatus=in-progress, serviceId=null, contentProviderName=null, interfaceName=null), errors=null, message=null)
You are probably using AssertJ because I don't think JUnit 4 or Hamcrest have a single argument version of assertThat(), and JUnit 5 doesn't have such method.
The way AssertJ assertThat() works is that the first argument of the call is the actual value that you want to perform assertions on. This returns an assertion object, which provides you a number of assertion methods.
When you call assertThat(expectedEMSResponse==eMSResponse), AssertJ just returns an assertion object for boolean type and nothing else happens.
The correct way to use it would be:
assertThat(eMSResponse).isEqualTo(expectedEMSResponse);
Now it returns an assertion object for the first argument, and then performs the isEqualTo() assertion on that one. The assertThat() call only works as an entry point to different assertions that can be performed on the first argument.
I don't think you are using assertThat correctly. Looks like you need to pass additional arguments as well. These are the method definitions:
assertThat(T actual, Matcher<? super T> matcher)
assertThat(String reason, T actual, Matcher<? super T> matcher)
Also as per the documentation, it is deprecated. Might I suggest that you use assertEquals() instead?
The documentation: https://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertThat
This does not fail because you are not using junit correctly:
#Test
public void processJMSRequestForHappyPathTest() throws Exception {
// test case logic here
// mock logic here
EMSResponse emsResponse = processor.processJMSRequest(request);
System.out.println(" expectedEMSResponse ..." + EMSResponse.toString());
System.out.println(" processJMSRequestForHappyPathTest expectedEMSResponse ...:" + expectedEMSResponse.toString());
System.out.println("check if i am equal..."+ expectedEMSResponse.equals(emsResponse));
assertThat(expectedEMSResponse==eMSResponse);
}
You should have:
assertTrue(expectedEMSResponse==eMSResponse);
assertEquals(expectedEMSResponse, eMSResponse);
Or using assertThat with assertj:
assertThat(eMSResponse).isEqualTo(expectedEMSResponse);
This should help you understand:
junit 5: https://junit.org/junit5/docs/current/user-guide/
junit 4: https://www.baeldung.com/junit-assertions
The context
I have a simple method that I'm testing using the mockito library.
The problem
I have a error:
"[MockitoHint] ReceiveServiceTest.testGetFileDto (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at .ReceiveServiceTest.testGetFileDto(ReceiveServiceTest.java:46)
[MockitoHint] ...args ok? -> at ReceiveService.getFileDto(ReceiveService.java:28)
I dont understand way.
The code
#RunWith(MockitoJUnitRunner.class)
public class ReceiveServiceTest {
private List<File> filePaths = new ArrayList<>();
#InjectMocks
private ReceiveService receiveService;
#Mock
private FindFiles findfiles;
#Mock
private ReadByte readByte;
#Before
public void before() {
filePaths.add(new File("d://folder//test1_message_received"));
filePaths.add(new File("d://folder//test2_message_received"));
filePaths.add(new File("d://folder//test3_message_received"));
}
#Test
public void testGetFileDto() throws IOException {
// Given
byte[] resultByteArr = new byte[1028];
when(findfiles.getPathFiles()).thenReturn(filePaths);
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
List<MessageDTO> result = receiveService.getFileDto();
//some assert
}
method
#Autowired
private FindFiles findFiles;
#Autowired
private ReadByte readByte;
public List<MessageDTO> getFileDto() throws IOException {
List<MessageDTO> fileDtos = new ArrayList<>();
for (File file : findFiles.getPathFiles()) {
fileDtos.add(new MessageDTO(Base64.getEncoder().encode(readByte.readByteArrFromFile(new File(file.getPath()))),
file.getName(), "zip", null));
}
return fileDtos;
}
I think mocks are not being initialized. Please initialize the mocks in the #Before method.
#Before
public void init() {
initMocks(this);
}
This should solve the problem I guess.
Here is solution for my problem. I added foreach loop. Now the mock works, but byte [] is different than what it should return.
// Given
byte[] mockByteArr = new byte [2048];
when(findfiles.getPathFiles()).thenReturn(filePaths);
for (File filePath : filePaths) {
when(readByte.readByteArrFromFile(new File(filePath.getPath()))).thenReturn(mockByteArr);
}
//When
List<MessageDTO> result = receiveService.getFileDto();
//Then
assertEquals(3, result.size());
assertEquals(mockByteArr, result.get(1).getContent());
Your problem is, that you create a new object in the following line:
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
Mockito needs to know which real object is passed to the method so that it can return the appropriate thenReturn-value. So if you pass the actual reference into it, your code will work, but also only if you specify all the values which are listed. Otherwise you may get a NullPointerException.
By the way, calling new File(file.getPath()) seems redundant to me. You can just use file instead.
So with the following your code might work better:
when(readByte.readByteArrFromFile(filePaths.get(0)).thenReturn(resultByteArray);
but then you need to specify it for all entries.
Alternatively, use a Matcher instead:
when(readByte.readByteArrFromFile(ArgumentMatchers.any(File.class))).thenReturn(resultByteArr);
or specify the actual argument matching you require as matchers can be very powerful in that regard.
Previously the answer contained the following, which is still true, but not as concise as the answer above:
It's been a long time since I last used mocks (and I am even proud of it ;-)).
The message already states that one should consult the javadoc and there I found the following:
Those are hints - they not necessarily indicate real problems 100% of the time.
Nonetheless, I believe the problem is with the following statement:
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
I think you need to specify a return for every entry in the filePaths or make the call more generic using Matchers.any() (or any other appropriate Matcher).
I am trying to use PowerMockito to mock the creation of the java.net.URL class in my code that I'm testing. Basically, I want to prevent the real HTTP request from occurring and instead 1) check the data when the request is made and 2) supply my own test data back on a mocked response. This is what I'm trying:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ URL.class, MockedHttpConnection.class })
public class Test {
URL mockedURL = PowerMockito.mock(URL.class);
MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class);
...
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL);
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
...
}
The code that I want to test looks like this:
URL wlInvokeUrl = new URL(wlInvokeUrlString);
connection = (HttpURLConnection) wlInvokeUrl.openConnection();
Earlier in my test scenario I mock the wlInvokeUrlString to match "MyURLString". I've also tried using various other forms of the whenNew line, trying to inject the mock. No matter what I try, it never intercepts the constructor. All I want to do is "catch" the call to openConnection() and have it return my mocked HTTP connection instead of the real one.
I have mocked other classes ahead of this one in the same script and these are working as expected. Either I need a second pair of eyes (probably true) or there is something unique about the URL class. I did notice that if I use "whenNew(URL.class).withAnyArguments()" and change the "thenReturn" to "thenAnswer" I could get it to trigger. Only problem is I never get the URL call for my code. What I see is an invocation of the 3-argument constructor for URL.class with all nulls for the parameters. Could it be this class is from the Java runtime and is bootstrapped by the test runner? Any help is much appreciated.
It's a common mistake when use PowerMockito.whenNew.
Note that you must prepare the class creating the new instance of MyClass for test, not the MyClass itself. E.g. if the class doing new MyClass() is called X then you'd have to do #PrepareForTest(X.class) in order for whenNew to work
From Powermock wiki
So, you need a bit change your test and add to #PrepareForTesta class which create a new instance of URLlike:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ URL.class, MockedHttpConnection.class , ConnectionUser.class})
public class URLTest {
public class URLTest {
private ConnectionUser connectionUser;
#Before
public void setUp() throws Exception {
connectionUser = new ConnectionUser();
}
#Test
public void testName() throws Exception {
URL mockedURL = PowerMockito.mock(URL.class);
MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class);
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL);
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
connectionUser.open();
assertEquals(mockedConnection, connectionUser.getConnection());
}
}
where:
public class ConnectionUser {
private String wlInvokeUrlString = "MyURLString";
private HttpURLConnection connection;
public void open() throws IOException {
URL wlInvokeUrl = new URL(wlInvokeUrlString);
connection = (HttpURLConnection) wlInvokeUrl.openConnection();
}
public HttpURLConnection getConnection() {
return connection;
}
}
I'm not sure the difference between .withParameterTypes(x) and .withArguments(x) but I believe you need to set it up as follows for your code to work. Give it a try and let me know if this doesn't help.
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
PowerMockito.whenNew(URL.class).withArguments(Mockito.anyString()).thenReturn(mockedURL);
The problem is that the arguments of the real call are not matching with the expected in your mock.
PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL) will return mockedURL only the call is new URL("MyURLString").
If you change it to:
PowerMockito.whenNew( URL.class ).withParameterTypes( String.class )
.withArguments( org.mockito.Matchers.any( String.class ) ).thenReturn( mockedURL );
It will catch any string passed to the constructor URL(String) (even null) and return your mock.
When you tried
"whenNew(URL.class).withAnyArguments()" and change the "thenReturn" to
"thenAnswer" I could get it to trigger. Only problem is I never get
the URL call for my code. What I see is an invocation of the
3-argument constructor for URL.class with all nulls for the
parameters.
PowerMock will try to mock all constructors (org.powermock.api.mockito.internal.expectation.DelegatingToConstructorsOngoingStubbing.InvokeStubMethod at line 122) then it calls the first one (with 3 arguments) and mock its answer. But the subsequent calls will return the already mocked one because you told it to mock for any arguments. That's why you see just one call with null, null, null in your Answer.
Hi i have method insertOrUpdateProductsToDB(Product product) is used to perform insert operation in database using catalogService of Broadleaf ,catalog Service is doing all saving operation in db . My method is expected restClient product as a parameter.After passing the restClient product we are converting this product into Broadleafproduct by using ProductConversion Class.In product conversion only setting is happening for converting rest Product into broadleafproduct. Now my requirement is to test this method using mockito but when i tried to do add these two line at the end of my test method
verify(mainProduct).getAdditionalSkus().add(sku);
verify(mainProduct).setProductOptions(productOptionList);
Its failing.
when i debug the code there is for loop inside for loop in the method insertOrUpdateProductsToDB(Product product) and i find productOption = catalogService.saveProductOption(productOption); here productOption is coming null so please tell how to test loop inside loop and same happening for
for (Sku skuWithProductOptions : productConversion.createSkuWithProductOptions(product, mainProduct,productOptionList)) {
catalogService.saveSku(skuWithProductOptions);
}
this line in the same method .kindly also check my test case whether i am doing right or not .
Class and insertOrUpdateProductsToDB(Product product) Method to be test
import com.admin.exception.AdminGenericException;
import com.admin.exception.AdminRestException;
import com.admin.util.helper.ProductConversion;
import com.admin.wrapper.getproducts.req.ObjectFactory;
import com.admin.wrapper.getproducts.resp.Product;
import com.admin.wrapper.getproducts.resp.Response;
import com.mycompany.rest.service.client.RestClientUtil;
import com.mycompany.util.constants.ApplicationConstants;
#Service
public class GetProductsServiceImpl {
private static final Logger logger = Logger.getLogger(GetProductsServiceImpl.class);
#Resource(name = "blCatalogService")
protected CatalogService catalogService;
public void setCatalogService(CatalogService catalogService) {
this.catalogService = catalogService;
}
protected RestClientUtil restClientUtil;
public void setRestClientUtil(RestClientUtil restClientUtil) {
this.restClientUtil = restClientUtil;
}
#Value("#{configProperties['salePriceRate']}")
private long salePriceRate;
public void setRetailPriceRate(long retailPriceRate) {
this.retailPriceRate = retailPriceRate;
}
#Value("#{configProperties['retailPriceRate']}")
private long retailPriceRate;
public void setSalePriceRate(long salePriceRate) {
this.salePriceRate = salePriceRate;
}
//Insertion/Update DB logic
public String insertOrUpdateProductsToDB(Product product) {
logger.debug("Start of : insertOrUpdateProductsToDB()");
try {
List<String> category = new ArrayList<String> (Arrays.asList(ApplicationConstants.CATEGORY));
ProductConversion productConversion = new ProductConversion();
List<ProductOption> productOptionList = new ArrayList<ProductOption>();
if (category.contains(product.getCategory().toUpperCase())) {
org.broadleafcommerce.core.catalog.domain.Product mainProduct=catalogService.createProduct(new ProductType("org.broadleafcommerce.core.catalog.domain.Product", "Normal Product"));
mainProduct = productConversion.createProduct(mainProduct,product);
Sku sku=catalogService.createSku();
mainProduct.setDefaultSku(sku);
mainProduct = productConversion.addSkuToProduct(mainProduct, product, salePriceRate,retailPriceRate);
for (ProductOption productOption : productConversion.createProductOptions(product, mainProduct)) {
productOption.setAllowedValues(productConversion.createProductOptionValues(product,productOption));
productOption = catalogService.saveProductOption(productOption);
productOptionList.add(productOption);
}
sku = catalogService.saveSku(mainProduct.getDefaultSku());
mainProduct.getAdditionalSkus().add(sku);
mainProduct.setProductOptions(productOptionList);
mainProduct = catalogService.saveProduct(mainProduct);
for (Sku skuWithProductOptions : productConversion.createSkuWithProductOptions(product, mainProduct,productOptionList)) {
catalogService.saveSku(skuWithProductOptions);
}
}
logger.debug("End of : insertOrUpdateProductsToDB()");
return "Product inserted into DB successfully";
}
catch (Exception e) {
logger.error("Error:", e);
return "Insertion of product into DB Failed ";
}
}
//Insertion service for DB
public String insertProductsIntoDB(){
logger.debug("Start of : insertProductsIntoDB()");
int insertionCount=0;
try{
com.admin.wrapper.getproducts.resp.Response resp = getAvailableProductsFromPBS();
for (Product product : resp.getProducts().getProduct()) {
if(catalogService.findProductById(Long.parseLong(product.getId()))==null){
String str=insertOrUpdateProductsToDB(product);
if(str.equalsIgnoreCase("Product inserted into DB successfully")){
insertionCount=insertionCount+1;
}
}
}
logger.debug(insertionCount+" Products inserted into DB successfully");
logger.debug("End of : insertProductsIntoDB()");
return insertionCount+" Products inserted into DB successfully";
}catch (AdminRestException e) {
logger.error("Error:", e);
return e.getMessage();
}
}
}
My test case class and method
public class GetProductsServiceImplTest {
private CatalogService catalogService;
private RestClientUtil restClientUtil;
private GetProductsServiceImpl getProductsServiceImpl;
private org.broadleafcommerce.core.catalog.domain.Product mainProduct;
private Sku sku;
private ProductOption productOption;
private List<ProductOption> productOptionList;
#Before
public void setUp() throws Exception {
catalogService = mock(CatalogService.class);
productOptionList=mock(List.class);
mainProduct = spy(new ProductImpl());
sku = new SkuImpl();
getProductsServiceImpl = new GetProductsServiceImpl();
getProductsServiceImpl.setCatalogService(catalogService);
productOption=mock(ProductOption.class);
restClientUtil = new RestClientUtil();
}
#Test
public void testInsertOrUpdateProductsToDB() {
restClientUtil.setSellerCode("1");
restClientUtil.setPbsUrl("http://10.52.165.239:8080/pbs");
getProductsServiceImpl.setRestClientUtil(restClientUtil);
Response pbsResponse = getProductsServiceImpl
.getAvailableProductsFromPBS();
for (Product pbsProduct : pbsResponse.getProducts().getProduct()) {
when(catalogService.createProduct(new ProductType("org.broadleafcommerce.core.catalog.domain.Product","Normal Product"))).thenReturn(mainProduct);
when(catalogService.createSku()).thenReturn(sku);
when(catalogService.saveProductOption(productOption)).thenReturn(productOption);
when(catalogService.saveSku(sku)).thenReturn(sku);
when(catalogService.saveProduct(mainProduct)).thenReturn(mainProduct);
when(catalogService.saveSku(sku)).thenReturn(sku);
getProductsServiceImpl.insertOrUpdateProductsToDB(pbsProduct);
verify(mainProduct,times(2)).setDefaultSku(sku);
verify(mainProduct).getAdditionalSkus().add(sku);
verify(mainProduct).setProductOptions(productOptionList);
break;
}
}
}
This is the error while testing
java.lang.NullPointerException
at com.admin.api.service.getproducts.test.GetProductsServiceImplTest.testInsertOrUpdateProductsToDB(GetProductsServiceImplTest.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
I have a few remarks that probably won't answer your orignal question. But I hope they will guide you toward a better refactor of this code. Also the code sample you showed are not enough to point you at the exact issue ; it's an NPE in the test method so it should not be that difficult to track down.
That being said here's the point I'd like to raise
The test code is curiously crafted, and in my opinion this code are overusing Mockito. Overall this code looks way too complex to be correctly tested anyway. I don't think it was coded following TDD principle (TDD is really convenient when it comes to testing and designing the app)
You may want to follow the common guideline no more than 10 line of codes in a single method, this usually helps to separate concerns and identify simpler code / intents. These simpler code could be changed and tested more easily if designed correctly (without leaking concepts or variables). For example you may want to extract a method that saves a single Product and test only that one.
What's even more striking is that this code seems kinda procedural (even if inside objects). And doesn't really explain the intent in business words (ok it's about saving stuff in DB, but for which reason there's all this logic, this reason should appear in the method name).
The test and Mockito is weird, and the code should not iterate over the collection to stub then verify
for (Product pbsProduct : pbsResponse.getProducts().getProduct()) {
when(catalogService.createProduct(new ProductType("org.broadleafcommerce.core.catalog.domain.Product","Normal Product"))).thenReturn(mainProduct);
when(catalogService.createSku()).thenReturn(sku);
when(catalogService.saveProductOption(productOption)).thenReturn(productOption);
when(catalogService.saveSku(sku)).thenReturn(sku);
when(catalogService.saveProduct(mainProduct)).thenReturn(mainProduct);
when(catalogService.saveSku(sku)).thenReturn(sku);
getProductsServiceImpl.insertOrUpdateProductsToDB(pbsProduct);
verify(mainProduct,times(2)).setDefaultSku(sku);
verify(mainProduct).getAdditionalSkus().add(sku);
verify(mainProduct).setProductOptions(productOptionList);
break;
}
In pseudo code I would first try to extract the saving logic using the given/when/then BBDD keywords (they help to clarify what need to be tested in which scenario and context). Keep the fixture and assertions to a minimum, you would rather deal with multiple test method than multiple complex test methods.
#Test
public void ensure_product_is_saved_in_the_catalog() {
// given
Product a_simple_product = ProductBuilder.simpleProduct().build();
when(catalogService.doSomething(....))).thenReturn(mainProduct);
// when
productsService.saveProduct(product);
// then
verify(catalogService).doSomethingElseWith(mainProduct);
}
If assertion on product data is relevant in your test scenario, then write a test that actually test the data (using JUnit assertions, AssertJ, ...). Don't mock the Product !
And proceed gradually for each test, then refactor if need ed to keep the code manageable (extract a single method in another class if necessary, etc.)
You should definitely read the following books, they've helped a lot of programmers to get better code Clean Coder or Growing Object Oriented Software, Guided by Tests. This list is of course not exhaustive.
Hope that helps.
I'm writing unit tests in JUnit, but have not been able to successfully cover a branch of a particular method that catches a SQLException and returns a null object.
This is the class I'm testing:
#Component
public class UnitOfMeasureRowMapper implements RowMapper<UnitOfMeasure> {
public UnitOfMeasure mapRow(final ResultSet resultSet, final int rowNumber) throws SQLException {
UnitOfMeasure unitOfMeasure = new UnitOfMeasure();
try {
unitOfMeasure.setUnitOfMeasureId(resultSet.getInt("UNITOFMEASUREID"));
unitOfMeasure.setOwnerUserId(resultSet.getInt("USERID"));
unitOfMeasure.setName(resultSet.getString("NAME"));
unitOfMeasure.setDescription(resultSet.getString("DESCRIPTION"));
} catch (SQLException e) {
unitOfMeasure = null;
}
return unitOfMeasure;
}
}
This is the JUnit test that I have written to cover the second branch of the above method (with appropriate context from the test class):
private static UnitOfMeasure testUnitOfMeasure;
private static UnitOfMeasureRowMapper mockRowMapper;
public void setUp() throws Exception {
mockRowMapper = mock(UnitOfMeasureRowMapper.class);
mockResultSet = mock(ResultSet.class);
}
#Test(expected=SQLException.class)
public void testUnitOfMeasureRowMapperFailsSQLException() throws SQLException {
when(mockRowMapper.mapRow(mockResultSet, 1)).thenReturn(null);
testUnitOfMeasure = mockRowMapper.mapRow(mockResultSet, 1);
}
I think the problem is with the last line; somehow I need to force a SQLException. The problem is, I don't know how and haven't been able to find an answer. Can anyone help?
If I understand the question well, the class under test is UnitOfMeasureRowMapper. If this is true, then you don't want to mock it in your test, otherwise you are testing a mock!
What is under test in your JUnit, is the behavior of UnitOfMeasureRowMapper#mapRow when ResultSet you give it throws a SQLException during the execution of the method. Then you want this method to return null.
I would write it like this:
private ResultSet mockResultSet;
private RowMapper<UnitOfMeasure> rowMapper = new UnitOfMeasureRowMapper();
public void setUp() throws Exception {
mockResultSet = mock(ResultSet.class);
}
#Test
public void mapRow_SHOULD_return_null_WHEN_resultSet_throws_a_SQLException() {
when(mockResultSet.getInt(anyString()).thenThrow(new SQLException());
assertThat(mockRowMapper.mapRow(mockResultSet, 1), nullValue());
}
As suggested Samuel in his answer, you may set one of the method of the result set you use to throw a SQLException, and then check in your JUnit that the mapRow method returns null as expected. Here you are not testing the behavior of the result set, so its fine to mock it to achieve a behavior it would normally have under some circunstancies that would be painful to obtain otherwise. Mocking the result set behavior lets you focus on testing the RowMapper behavior.
You are testing the UnitOfMeasureRowMapper that implements RowMapper. So have a rowMapper property in your JUnit, and I prefer to see it through its interface. I like to brutally call the constructor of UnitOfMeasureRowMapper because I want to keep my JUnit as simple as they can be.
Set one of the methods (maybe getInt?) of your mock ResultSet to throw the exception. You didn't specify what mocking framework you're using so I can't tell you the exact syntax.