英文:
calling method on object returned by when().thenReturn()
问题
经过大量的研究,我在Java的JUnits中没有找到这个问题的答案。
我想做的是:在when().thenReturn(object)调用返回的对象上调用一些方法。
例如:
public boolean checkUpdate(String str, String endStr){
GetEndpointRequest geaReq = new GetEndpointRequest().withEndpointArn(endpointArn);
GetEndpointResult geaRes = amazonSNS.getEndpointAttributes(geaReq);
return !geaRes.getAttributes().get("Token").equals(token) || !geaRes.getAttributes().get("Enabled").equalsIgnoreCase("true");
}
以下是测试方法:
import com.amazonaws.services.sns.model.GetEndpointAttributesRequest;
import com.amazonaws.services.sns.model.GetEndpointAttributesResult;
@Tested
AmazonSNSRegistrationService service = new AmazonSNSRegistrationService();
service.amazonSNS = mock(AmazonSNS.class);
@Test
public void checkUpdateTest(){
String pushToken = "dxbv1fwJYIo";
String strToken = "";
String strEnabled = "";
String endPointArn = "";
Map<String, String> jsonBody = new HashMap<String, String>();
jsonBody.put("Token", "");
jsonBody.put("enabled", "");
GetEndpointAttributesRequest getEndpointReq = mock(GetEndpointAttributesRequest.class);
GetEndpointAttributesResult getEndpointRes = mock(GetEndpointAttributesResult.class);
getEndpointRes.setAttributes(jsonBody);
when(service.amazonSNS.getEndpointAttributes(getEndpointReq)).thenReturn(getEndpointRes);
when(getEndpointRes.getAttributes()).thenReturn(jsonBody);
when(getEndpointRes.getAttributes().get(strToken)).thenReturn("");
when(getEndpointRes.getAttributes().get(strEnabled)).thenReturn("");
amazonSNSRegistrationService.checkUpdate(pushToken, endPointArn);
}
在checkUpdate()方法的这一行中我得到了NullPointerException:return !geaRes.getAttributes().get("Token").equals(token);
。
因为geaRes为null。
如何解决这个问题?
添加另一个简单的示例,我在这个问题上遇到了这个问题:
AmazonSNSRegistrationSerice.java:
public boolean deletePlatformApplicationArn(String deviceId, String appId){
boolean isArnDeleted = false;
try {
DeleteEndpointRequest deleteEndpointReq = new DeleteEndpointRequest().withEndpointArn(appId);
DeleteEndpointResult result = amazonSNS.deleteEndpoint(deleteEndpointReq);
if (result.getSdkHttpMetadata().getHttpStatusCode() == HttpStatus.SC_OK) {
AWSUtil.deleteArnEndpoint(deviceId, appId);
isArnDeleted = true;
}
} catch (Exception e) {
ErrorLogEventHelper.logErrorEvent(this.getClass().getName(), "Exception while deleting AWS ARN (endpoint)" + e.getMessage(), "deletePlatformApplicationArn", e, ErrorLogEvent.ERROR_SEVERITY);
}
return isArnDeleted;
}
AmazonSNSRegistrationSericeTest.java:
@Test
public void deletePlatformApplicationArnTest(){
String appId = "arn:aws:sns";
String deviceId = "dev_1";
DeleteEndpointRequest deleteEndpointReq = mock(DeleteEndpointRequest.class);
DeleteEndpointResult result = mock(DeleteEndpointResult.class);
when(amazonSNSRegistrationService.amazonSNS.deleteEndpoint(deleteEndpointReq)).thenReturn(result);
SdkHttpMetadata metadata = mock(SdkHttpMetadata.class);
when(result.getSdkHttpMetadata()).thenReturn(metadata);
when(result.getSdkHttpMetadata().getHttpStatusCode()).thenReturn(HttpStatus.SC_OK);
amazonSNSRegistrationService.deletePlatformApplicationArn(deviceId, appId);
}
再次出现在if(result.getSdkHttpMetadata())这一行的result对象为空。
英文:
After doing lots of research, I didn't find the answer for this question in JUnits in java.
What I want to do is: To call some method on object returned by when().thenReturn(object) call.
e.g.:
<!-- language-all: lang-java -->
public boolean checkUpdate(String str, String endStr){
GetEndpointRequest geaReq = new
GetEndpointRequest().withEndpointArn(endpointArn);
GetEndpointResult geaRes = amazonSNS.getEndpointAttributes(geaReq);
return !geaRes.getAttributes().get("Token").equals(token) || !geaRes.getAttributes().get("Enabled").equalsIgnoreCase("true");
}
And here is the Test method:
import com.amazonaws.services.sns.model.GetEndpointAttributesRequest;
import com.amazonaws.services.sns.model.GetEndpointAttributesResult;
@Tested
AmazonSNSRegistrationService service= new AmazonSNSRegistrationService();
service.amazonSNS = mock(AmazonSNS.class);
@Test
public void checkUpdateTest(){
String pushToken = "dxbv1fwJYIo";
String strToken = "";
String strEnabled = "";
String endPointArn = "";
Map<String, String> jsonBody = new HashMap<String, String>();
jsonBody.put("Token", "");
jsonBody.put("enabled", "");
GetEndpointAttributesRequest getEndpointReq =mock(GetEndpointAttributesRequest.class);
GetEndpointAttributesResult getEndpointRes =mock(GetEndpointAttributesResult.class);
getEndpointRes.setAttributes(jsonBody);
when(service.amazonSNS.getEndpointAttributes(getEndpointReq)).thenReturn(getEndpointRes);
when(getEndpointRes.getAttributes()).thenReturn(jsonBody);
when(getEndpointRes.getAttributes().get(strToken)).thenReturn("");
when(getEndpointRes.getAttributes().get(strEnabled)).thenReturn("");
amazonSNSRegistrationService.checkUpdate(pushToken, endPointArn);
}
I'm getting NullPointerException in checkUpdate() method on line -- "return !geaRes.getAttributes().get("Token").equals(token)".
As, geaRes is null.
How to solve this?
Adding another simple example , where I face this issue:
AmazonSNSRegistrationSerice.java:
public boolean deletePlatformApplicationArn(String deviceId, String appId){
boolean isArnDeleted = false;
try {
DeleteEndpointRequest deleteEndpointReq = new DeleteEndpointRequest().withEndpointArn(appId);
DeleteEndpointResult result = amazonSNS.deleteEndpoint(deleteEndpointReq);
if (result.getSdkHttpMetadata().getHttpStatusCode() == HttpStatus.SC_OK) {
AWSUtil.deleteArnEndpoint(deviceId, appId);
isArnDeleted = true;
}
} catch (Exception e) {
ErrorLogEventHelper.logErrorEvent(this.getClass().getName(), "Exception while deleting AWS ARN (endpoint)" + e.getMessage(), "deletePlatformApplicationArn", e, ErrorLogEvent.ERROR_SEVERITY);
}
return isArnDeleted;
}
AmazonSNSRegistrationSericeTest.java:
@Test
public void deletePlatformApplicationArnTest(){
String appId = "arn:aws:sns";
String deviceId = "dev_1";
DeleteEndpointRequest deleteEndpointReq = mock(DeleteEndpointRequest.class);
DeleteEndpointResult result = mock(DeleteEndpointResult.class);
when(amazonSNSRegistrationService.amazonSNS.deleteEndpoint(deleteEndpointReq)).thenReturn(result);
SdkHttpMetadata metadata = mock(SdkHttpMetadata.class);
when(result.getSdkHttpMetadata()).thenReturn(metadata);
when(result.getSdkHttpMetadata().getHttpStatusCode()).thenReturn(HttpStatus.SC_OK);
amazonSNSRegistrationService.deletePlatformApplicationArn(deviceId, appId);
}
Again the result object in if(result.getSdkHttpMetadata()) is coming NULL.
答案1
得分: 0
首先,您试图从JsonMap中获取一个空值:
@Test
public void checkUpdateTest(){
String strToken = "";
.
.
.
when(getEndpointRes.getAttributes().get(strToken)).thenReturn("");
// 所以你在这里实际上是这样的:
when(getEndpointRes.getAttributes().get("")).thenReturn("");
// 这应该是 "Token"
when(getEndpointRes.getAttributes().get("Token")).thenReturn("");
}
接下来,JsonMap是一个真实的对象,而不是一个模拟对象,所以您不需要调用 when(getEndpointRes.getAttributes().get(strToken)).thenReturn("");
,因为您的JsonMap将返回正确的值。
然后,由于您的 getEndpointRes
是一个模拟对象,所以不需要这个调用。这就是为什么您使用了 when().thenX() 的语法:
getEndpointRes.setAttributes(jsonBody);
为了解决空指针异常的问题,尝试以下操作:
// 导入...
@Tested
AmazonSNSRegistrationService service = new AmazonSNSRegistrationService();
@Test
public void checkUpdateTest(){
// 在这里直接模拟 AmazonSNS
AmazonSNS amazonSNS = mock(AmazonSNS.class);
service.amazonSNS = amazonSNS;
String pushToken = "dxbv1fwJYIo";
String endPointArn = "";
// 也许这些也应该模拟
Map<String, String> jsonBody = new HashMap<String, String>();
jsonBody.put("Token", "");
jsonBody.put("enabled", "");
// 删除了模拟请求。在这种情况下,您的服务会自行构建它,不需要它
GetEndpointAttributesResult getEndpointRes = mock(GetEndpointAttributesResult.class);
getEndpointRes.setAttributes(jsonBody);
// 直接使用模拟对象,并对任何 getEndpointAttributes 调用进行反应,因为在您的服务中 getEndpointReq 永远不会存在!
when(amazonSNS.getEndpointAttributes(any())).thenReturn(getEndpointRes);
when(getEndpointRes.getAttributes()).thenReturn(jsonBody);
amazonSNSRegistrationService.checkUpdate(pushToken, endPointArn);
// 进行一些进一步的断言
}
看起来您试图模拟您不需要模拟的东西,并模拟在您的服务中生成的东西。如果您在要测试的类中遇到空指针异常,应该仔细调试您的测试。在您的情况下,模拟似乎是不正确的,因此实际对象的调用导致了空指针异常。
在您的第二个示例中,问题似乎是相同的。您试图从一个模拟对象中返回某些值,而这个模拟对象可能是在您的 amazonSNSRegistrationService
内部创建的。如果它不是类的一部分,您的模拟的 deleteEndpointReq
如何返回任何值?您只是创建了模拟对象,但它并不是在您的服务内部创建的那个对象!
最后,您应该添加一些断言。我在这里看到的测试只有在被测试的类出现错误(就像在您的情况下)时才会失败。但是缺少一些基本的断言。我个人在单元测试中使用 AAA 模式。
英文:
So first of all, you're trying to get an empty value out of your JsonMap:
@Test
public void checkUpdateTest(){
String strToken = "";
.
.
.
when(getEndpointRes.getAttributes().get(strToken)).thenReturn("");
// so what you ask here is this:
when(getEndpointRes.getAttributes().get("")).thenReturn("");
// this should probably be "Token"
when(getEndpointRes.getAttributes().get("Token")).thenReturn("");
}
the next thing is, the JsonMap is a real object and not a mock so you don't need to call when(getEndpointRes.getAttributes().get(strToken)).thenReturn("");
as your jsonmap is going to return the right value.
then, this call is not needed as your getEndpointRes is a mock. That's why you use the when().thenX() syntax
getEndpointRes.setAttributes(jsonBody);
To resolve your NPE problem try the following:
// imports...
@Tested
AmazonSNSRegistrationService service= new AmazonSNSRegistrationService();
@Test
public void checkUpdateTest(){
// directly mock the amazonsns here
AmazonSNS amazonSNS = mock(AmazonSNS.class);
service.amazonSNS = amazonSNS;
String pushToken = "dxbv1fwJYIo";
String endPointArn = "";
// maybe mock them aswell
Map<String, String> jsonBody = new HashMap<String, String>();
jsonBody.put("Token", "");
jsonBody.put("enabled", "");
// deleted the mocked request. You don't need it in this case as your service contructs it by itself
GetEndpointAttributesResultgetEndpointRes getEndpointRes = mock(GetEndpointAttributesResult.class);
getEndpointRes.setAttributes(jsonBody);
// directly use the mocked object and react on any() as getEntpointReq never will be present in your service!
when(amazonSNS.getEndpointAttributes(any())).thenReturn(getEndpointRes);
when(getEndpointRes.getAttributes()).thenReturn(jsonBody);
amazonSNSRegistrationService.checkUpdate(pushToken, endPointArn);
// do some further assertments
}
It seems that you try to mock things that you don't need to mock and mock things that are generated in your service. If you ever encounter a NPE in the classes you are trying to test you should debug your test properly. In your case the mocking seems incorrect and therefore the real object calls result in NPEs
In your second example it seems to be the same issue. you try to return something from an mock, that is probably created inside your amazonSNSRegistrationService
. How should your mocked deleteEndpointReq
return any value if it is not part of the class? You simple create the mock, but it is not the same object that is created inside of you service!
And after all you should add some assertions. The tests I see here only fail if the tested class fails with an error like in your case. But some basic assertions are missing. I personally use the AAA pattern for unit tests.
专注分享java语言的经验与见解,让所有开发者获益!
评论