I have an interface defined as follows:
public interface HttpClient {
public <T> UdemyResponse<T> get(Request request,
JSONUnmarshaler<T> unmarshaller, Gson gson)
throws UdemyException, IOException;
}
I have a class that implements the interface:
public class OkHttp implements HttpClient {
public OkHttpClient client;
final Logger logger = LoggerFactory.getLogger(getClass());
public OkHttp() {
this.client = new OkHttpClient();
}
#Override
public <T> UdemyResponse<T> get(Request request, JSONUnmarshaler<T> unmarshaller, Gson gson)
throws UdemyException, IOException {
int status_code = 0;
String next = null;
String rawJSON = null;
JsonElement jsonelement = null;
Boolean retry = true;
int attempts = 3;
while ((attempts >= 0) && (retry) && status_code != 200) {
try {
Response response = this.client.newCall(request).execute();
rawJSON = response.body().string();
jsonelement = gson.fromJson(rawJSON, JsonElement.class);
next = gson.fromJson(jsonelement.getAsJsonObject().get("next"), String.class);
status_code = response.code();
if (status_code == 401) {
try {
logger.warn("token expired");
TimeUnit.SECONDS.sleep(5);
retry = true;
continue;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if ((status_code / 100) == 5) {
logger.warn("gateway error");
retry = true;
continue;
}
} catch (IOException e) {
e.printStackTrace();
// this exception will be propagated to the main method and handled there to exit the program,
// this exception should end the program.
throw e;
}
attempts -= 1;
retry = false;
}
if (status_code != 200) {
throw new UdemyException();
}
return new UdemyResponse<T>(status_code, next, rawJSON,
unmarshaller.fromJSON(gson, jsonelement.getAsJsonObject()));
}
If I mock my interface I can write test cases for get() method but my get() method uses the this.client and I need to mock that object as well.
In this case, is it better to mock the OkHttp object rather than the interface?
If you are attempting to test get() then you should not mock that method, if you do, what is it that you are testing? You need to mock the other dependencies of get() to help you test it in isolation. In this case if this.client is a dependency of get(), this is what you need to mock.
Edited in response to question changes
This is terrible: (status_code / 100).
Test for the real status code there.
You should do the following:
Create a mock OkHttpClient.
Inject the mock into your test class using reflection.
test the get method.
You may want to change the mocking of the ok thing in the code below,
but you should be able to just use simple Mockito mocks for everything.
Here is some example code:
public class TestOkHttp
{
private static final String VALUE_JSON_STRING "some JSON string for your test";
private OkHttp classToTest;
#Mock
private ClassWithExecute mockClassWithExecute;
#Mock
private OkHttpClient mockOkHttpClient;
#Mock
private Response mockResponse;
#Mock
private ResponseBodyClass mockResponseBodyClass;
#Mock
private Request mockRequest;
private Gson testGson;
#Test
public void get_describeTheTest_expectedResults()
{
final JSONUnmarshaler<someclass> unmarshallerForThisTest = new JSONUnmarshaler<>()
// setup the mocking functionality for this test.
doReturn(desiredStatusCode).when(mockResponse).code();
classToTest.get()
}
#Before
public void preTestSetup()
{
MockitoAnnotations.initMocks(this);
classToTest = new OkHttp();
testGson = new Gson();
doReturn(mockResponse).when(mockClassWithExecute).execute();
doReturn(mockClassWithExecute).when(mockOkHttpClient).newCall(mockRequest);
doReturn(mockResponseBodyClass).when(mockResponse).body();
doReturn(VALUE_JSON_STRING).when(mockResponseBodyClass).string();
ReflectionTestUtils.setField(classToTest,
"client",
mockOkHttpClient);
}
}
Related
here is my code, AgentRest is not mocked in A class
class A {
public void t() throws IOException {
AgentRest agentRest = new AgentRest("127.0.0.1", 8888);
HttpResponse<TaskStatusResponse> a = agentRest.dataBackup(null); // not mock
}
}
#Slf4j
#PrepareForTest({A.class, SftpClientTest.class,AgentRest.class })
#RunWith(PowerMockRunner.class)
class SftpClientTest {
#Test
void getHome() throws Exception {
HttpResponse<TaskStatusResponse> httpResponse =
HttpResponse.<TaskStatusResponse>builder().code(0).body(TaskStatusResponse.builder().status("").build()).build();
AgentRest agentRest = PowerMockito.mock(AgentRest.class);
PowerMockito.whenNew(AgentRest.class).withAnyArguments().thenReturn(agentRest);
PowerMockito.when(agentRest.dataBackup(ArgumentMatchers.any())).thenReturn(httpResponse);
new A().t();
log.info("");
}
}
i have try a lot but still failed, PowerMockito.whenNew seams not working, and i have added all class to PrepareForTest
I have found the probelm is junit5 is not working with powermock, solution link: https://rieckpil.de/mock-java-constructors-and-their-object-creation-with-mockito/
here is my new code:
class A {
public void t() throws IOException {
AgentRest agentRest = new AgentRest("127.0.0.1", 8888);
HttpResponse<TaskStatusResponse> a = agentRest.dataBackup(null);
}
}
#Slf4j
class SftpClientTest {
#Test
void getHome() throws Exception {
try (MockedConstruction<AgentRest> mocked = mockConstruction(AgentRest.class)) {
HttpResponse<TaskStatusResponse> httpResponse =
HttpResponse.<TaskStatusResponse>builder().code(0).body(TaskStatusResponse.builder().status("").build()).build();
// every object creation is returning a mock from now on
AgentRest agentRest = new AgentRest("sa", 22);
when(agentRest.dataBackup(ArgumentMatchers.any())).thenReturn(httpResponse);
new A().t();
}
}
}
I am trying to write unit tests for repository while using MVVM pattern in android.
What i have is a repository which fetched data from the network using retrofit
public class ValidateCbuRepository {
private static ValidateCbuRepository single_instance = null;
private MutableLiveData<CBUValidationImageResponse> data = new MutableLiveData<>();
public static ValidateCbuRepository getInstance() {
if (single_instance == null)
single_instance = new ValidateCbuRepository();
return single_instance;
}
public MutableLiveData<CBUValidationImageResponse> processImage(String encodedString) {
JsonObject postParam = new JsonObject();
postParam.addProperty("image", encodedString);
Api service = RetrofitClientInstance.getRetrofitInstance().create(Api.class);
data.setValue(null);
HttpUrl httpUrl = HttpUrl.parse("some url");
Call<CBUValidationImageResponse> responseCall = service.getProcessedImage_cbu_validation(httpUrl.toString(),postParam);
responseCall.enqueue(new Callback<CBUValidationImageResponse>() {
#Override
public void onResponse(Call<CBUValidationImageResponse> call, Response<CBUValidationImageResponse> response) {
if(response.isSuccessful()) {
CBUValidationImageResponse res = response.body();
CBUValidationImageResponse cbuValidationImageResponse = res;
Log.i("CBU response ",""+cbuValidationImageResponse.toString());
cbuValidationImageResponse.setSuccess(true);
cbuValidationImageResponse.setShowProgres(false);
cbuValidationImageResponse.setError(false);
data.setValue(cbuValidationImageResponse);
}
}
#Override
public void onFailure(Call<CBUValidationImageResponse> call, Throwable t) {
CBUValidationImageResponse cbuValidationImageResponse = new CBUValidationImageResponse();
cbuValidationImageResponse.setError(true);
cbuValidationImageResponse.setShowProgres(false);
data.setValue(cbuValidationImageResponse);
t.printStackTrace();
}
});
return data;
}
}
The unit test part
#Mock
private Observer<CBUValidationImageResponse> observer;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testApiResponse_success() {
Api mockedApiInterface = Mockito.mock(Api.class);
Call<CBUValidationImageResponse> mockedCall = Mockito.mock(Call.class);
Mockito.when(mockedApiInterface.getProcessedImage_cbu_validation(any(),any())).thenReturn(mockedCall);
try {
Mockito.doAnswer(new Answer() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Callback<CBUValidationImageResponse> callback = invocation.getArgument(0);
CBUValidationImageResponse cbuValidationImageResponse = new CBUValidationImageResponse();
cbuValidationImageResponse.setCBU_code("some code");
cbuValidationImageResponse.setHeight(7);
cbuValidationImageResponse.setBreadth(7);
cbuValidationImageResponse.setLength(7);
callback.onResponse(mockedCall, Response.success(cbuValidationImageResponse));
// or callback.onResponse(mockedCall, Response.error(404. ...);
// or callback.onFailure(mockedCall, new IOException());
return null;
}
}).when(mockedCall).enqueue(any(Callback.class));
ValidateCbuRepository validateCbuRepository = new ValidateCbuRepository();
String encodedString= "";
validateCbuRepository.processImage(encodedString).observeForever(observer);
Getting a null pointer exception at validateCbuRepository.processImage(encodedString).observeForever(observer). Next step is to verify the observer.
I expect the test to pass. What am i doing wrong here?. I did something similar foe view model and the test passes with 100% code coverage.
The retrofit call is asynchronous. Is that the reason why it fails?
Edit : It seems livedata is null while testing causing NPE.
Why wont this compile:
MyOkHttpClient okClient = new MyOkHttpClient.Builder()
.addInterceptor(new AddCookiesInterceptor())
.addInterceptor(new ReceivedCookiesInterceptor()).build();
Incompatible types.
Required:
my.path.util.auth.MyOkHttpClient
Found:
okhttp3.OkHttpClient
This is MY class:
public class MyOkHttpClient extends okhttp3.OkHttpClient implements Authenticator {
private static int MAX_AUTHENTICATE_TRIES = 3;
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= MAX_AUTHENTICATE_TRIES) {
return null; // If we've failed 3 times, give up. - in real life, never give up!!
}
String credential = Credentials.basic(AUTHTOKEN_USERNAME, AUTHTOKEN_PASSWORD);
return response.request().newBuilder().header("Authorization", credential).build();
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
Based on your comments, you incorrectly believe that you are decorating OkHttpClient with custom authentication logic.
Instead, you are unnecessarily extending OkHttpClient and implementing the Authenticator interface. You can simply build the standard OkHttpClient with any custom authenticator you would like.
As such, this is more like what you actually want
public class MyAuthenticator implements Authenticator {
private static int MAX_AUTHENTICATE_TRIES = 3;
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= MAX_AUTHENTICATE_TRIES) {
return null; // If we've failed 3 times, give up. - in real life, never give up!!
}
String credential = Credentials.basic(AUTHTOKEN_USERNAME, AUTHTOKEN_PASSWORD);
return response.request().newBuilder().header("Authorization", credential).build();
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
And then when you build your client
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(new AddCookiesInterceptor())
.addInterceptor(new ReceivedCookiesInterceptor())
.authenticator(new MyAuthenticator())
.build();
IN order to test couchbase, I am trying to create 30K-specific documents in 15 minutes.
During the test, 6563 documents are created and then hangs. I have seen that it takes 2 minutes to create 0-3K thousand; 5 minutes to create between 3K-6K; and 5 minutes to create the final 6K -6.5K documents.
Example here.
I would appreciate help in understanding what I am doing wrong. The code is below:
public class ConnectionManager {
Logger logger = Logger.getLogger(getClass().getName());
private CouchbaseClient client;
public ConnectionManager() {
init();
}
public void init() {
try {
logger.info("Opening base connection.");
List<URI> hosts = Arrays.asList(new URI("http://127.0.0.1:8091/pools"));
String bucket = "default";
String password = "";
client = new CouchbaseClient(hosts, bucket, password);
} catch (Exception e) {
client = null;
throw new IllegalStateException(e);
}
}
#PreDestroy
public void destroy() {
logger.info("Closing base connection.");
if (client != null) {
client.shutdown();
client = null;
}
}
public CouchbaseClient getClient() {
return client;
}
}
public class DatabaseManager {
ConnectionManager cm;
public DatabaseManager() {
cm = new ConnectionManager();
}
public String addDocument(String result) {
CouchbaseClient c = cm.getClient();
JSONParameters j = new JSONParameters();
String id = UUID.randomUUID().toString();
Date today = new Date();
SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSSZ");
String date = DATE_FORMAT.format(today);
j.setTime(date);
j.setData(UUID.randomUUID().toString());
j.setSender_id(result);
j.setFlag(false);
Gson gson = new Gson();
String json = gson.toJson(j);
c.add(result, json);
return json;
}
public class DataBaseAddServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
for (int k = 0; k < 30000; k++) {
String id = UUID.randomUUID().toString();
DatabaseManager dbManager = new DatabaseManager();
dbManager.addDocument(id);
}
} catch (Exception e) {
resp.getOutputStream().println(e.getMessage());
resp.flushBuffer();
}
}
}
I think you missed a key point from the example you linked to: in the Servlet, DatabaseManager is injected via dependency injection and thus there's only one instance created.
In you code, you actually create a new DatabaseManager inside the loop, so you end up creating 30K CouchbaseClients. You are probably hitting a limit, and definitely wasting a lot of time and resources with that much extra clients.
Just moving the DatabaseManager dbManager = new DatabaseManager(); before the for loop should make things far better.
I have a series of web service method calls which all follow the below format. The only difference in each method is httpRequest.methodName(). Can anybody think of a way I can encapsulate the common logic? Also note that my environment is J2ME.
public Response webserviceCall(Request request) {
HTTPRequest httpRequest = new HTTPRequest(new ConnectionProperties());
String errorMessage = "";
String errorCode = "";
try {
// the only thing different
httpRequest.methodName();
} catch (SDKException e) {
errorMessage = e.getMessage();
errorCode = e.getErrorCode();
}
Error error = new Error(errorMessage,errorCode);
return new Response(error);
}
One alternative is to put that code in an abstract class, and change it to call an abstract method (name it process, for example):
abstract class BaseWebService {
public abstract Response process(HTTPRequest request) throws SDKException;
public Response webserviceCall(Request request) {
HTTPRequest httpRequest = new HTTPRequest(new ConnectionProperties());
String errorMessage = "";
String errorCode = "";
try {
process(httpRequest);
} catch (SDKException e) {
errorMessage = e.getMessage();
errorCode = e.getErrorCode();
}
Error error = new Error(errorMessage,errorCode);
return new Response(error);
}
}
Then make each of your services extend that class and implement the process method as needed
class OneService extends BaseWebService {
Response process(HTTPRequest request) throws SDKException{
return request.methodName();
}
}
For the records, this is the Template Method Pattern