I using Spring Android Rest Template to perform HTTP request. I was testing like so :
package com.mnubo.platform.android.sdk.internal.user.services.impl;
import com.mnubo.platform.android.sdk.models.users.User;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
#RunWith(RobolectricTestRunner.class)
#Config(emulateSdk = 18)
public class DummyTest {
private MyApiImpl myApi;
private MockRestServiceServer myapiMockServer;
#Before
public void setUp() throws Exception {
myApi = new MyApiImpl("ACCESS_TOKEN");//break here
myapiMockServer = MockRestServiceServer.createServer(myApi.getRestTemplate());
}
#Test
public void testRestClient() throws Exception {
final User expectedUser = new User();
expectedUser.setUsername("test");
expectedUser.setLastname("lastname");
myapiMockServer.expect(requestTo("http://my-service.com/user/test"))
.andExpect(method(GET))
.andRespond(withSuccess(this.convertToJsonString(expectedUser), APPLICATION_JSON_UTF8));
User user = myApi.userOperations().getUser("test");
userMockServer.verify();
}
}
All of this was working correctly using the RoboelectricTestRunner. But, yesterday Android Studio updated and asked me to update the build tool version to 1.1.0. (com.android.tools.build:gradle:1.1.0).
This version now include supports for Unit testing. See https://sites.google.com/a/android.com/tools/tech-docs/unit-testing-support
The problem : I can't create MyApiImpl anymore because it creates a RestTemplate. This RestTemplate use org.springframework.http.client.HttpComponentsClientHttpRequestFactory and in this class constructor, methods from the org.apache.http package are used.
These methods raised an exception : Eg: Method getSocketFactory in org.apache.http.conn.scheme.PlainSocketFactory
Well, I mocked successfully the getSocketFactory using PowerMock but then I had to mock the register method of SchemeRegistry that is only locally accessible from the constructor (source).
So I gave up trying to mock all of the shenanigans happening inside the RestTemplate and decided to directly mock the RestTemplate.
I need help to mock it correctly so that the MockRestServiceServer can still be used in my test, because right now, the myapiMockServer.verify() assertion fails.
Update
I'm still unable to use the MockRestServiceServer to test my Api, but I managed to test each individual OperationImpl using a mock of the RestTemplate like so :
public class DummyOperationTest {
private DummyOperation dummyOperation;
private RestTemplate mockedRestTemplate = mock(RestTemplate.class);
#Before
public void setUp() throws Exception {
super.setUp();
dummyOperation = new DummyOperationImpl(PLATFORM_BASE_URL, mockedRestTemplate);
}
#Test
public void test() throws Exception {
String calledUrl = PLATFORM_BASE_URL + "/objects?update_if_exists=true";
when(mockedRestTemplate.postForObject(calledUrl, expectedSmartObject, SmartObject.class)).thenReturn(expectedSmartObject);
smartObjectService.create(expectedSmartObject, true);
verify(mockedRestTemplate, atMost(1)).postForObject(calledUrl, expectedSmartObject, SmartObject.class);
}
}
Unfortunately, this still doesn't test the whole request execution. I can't validate that oAuth authentication is correctly added to the headers, or if the conversion of the server response is correct.
Well, it was a matter of mocking. I didn't understood everything correctly but I finally worked it out. Here is an a example that shows how to proceed :
package com.mnubo.platform.android.sdk.internal;
import android.util.Log;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
#RunWith(PowerMockRunner.class)
#PrepareForTest({
Log.class,
PlainSocketFactory.class,
SSLSocketFactory.class,
HttpComponentsClientHttpRequestFactory.class,
ConnManagerParams.class,
AbstractHttpClient.class
})
public class DummyTest {
private final String USER_ACCESS_TOKEN = "user_token";
private final PlainSocketFactory mockedPlainSocketFactory = mock(PlainSocketFactory.class);
private final SSLSocketFactory mockedSSLSocketFactory = mock(SSLSocketFactory.class);
private final SchemeRegistry mockedSchemeRegistry = mock(SchemeRegistry.class);
private final DefaultHttpClient mockedHttpClient = mock(DefaultHttpClient.class);
private final HttpParams mockedHttpParams = mock(HttpParams.class);
private DummyApiImpl dummyApi = new DummyApiImpl(USER_ACCESS_TOKEN);
protected MockRestServiceServer mockServer;
#Before
public void setUp() throws Exception {
mockStatic(Log.class);
mockStatic(PlainSocketFactory.class);
mockStatic(SchemeRegistry.class);
mockStatic(SSLSocketFactory.class);
mockStatic(ConnManagerParams.class);
whenNew(SchemeRegistry.class).withAnyArguments().thenReturn(mockedSchemeRegistry);
whenNew(DefaultHttpClient.class).withAnyArguments().thenReturn(mockedHttpClient);
when(PlainSocketFactory.getSocketFactory()).thenReturn(mockedPlainSocketFactory);
when(SSLSocketFactory.getSocketFactory()).thenReturn(mockedSSLSocketFactory);
when(mockedHttpClient.getParams()).thenReturn(mockedHttpParams);
mockServer = MockRestServiceServer.createServer(dummyApi.getRestTemplate());
}
#Test
public void doOperationTest() throws Exception {
final User testUser = new User();
mockUserServiceServer.expect(requestTo(expectedUrl("/users/test")))
.andExpect(method(POST))
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andRespond(withSuccess());
dummyApi.userOperations().createUser("test", testUser);
mockUserServiceServer.verify();
}
}
The problem occurred when the constructor of the Api was called. This classe extends AbstractOAuth2ApiBinding which constructor create a RestTemplate. This is this object that requires multiple level of mocking.
Once you have mocked the required element to create the RestTemplate, the rest is very easy, thanks to the mock server!
Related
I'm using Green Coffee library to run Cucumber scenarios in my instrumentation tests. I followed example provided by repo step-by-step, but here's the error:
junit.framework.AssertionFailedError: Class pi.survey.features.MembersFeatureTest has no public constructor TestCase(String name) or TestCase()
And when I try to add default constructor to the class like provided here, it says
no default constructor available in
'com.mauriciotogneri.greencoffee.GreenCoffeeTest'
Here's my test's source code:
package pi.survey.features;
import android.support.test.rule.ActivityTestRule;
import com.mauriciotogneri.greencoffee.GreenCoffeeConfig;
import com.mauriciotogneri.greencoffee.GreenCoffeeTest;
import com.mauriciotogneri.greencoffee.Scenario;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import pi.survey.MainActivity;
import pi.survey.steps.memberSteps;
#RunWith(Parameterized.class)
public class MembersFeatureTest extends GreenCoffeeTest {
#Rule
public ActivityTestRule<MainActivity> activity = new ActivityTestRule<>(MainActivity.class);
public MembersFeatureTest(Scenario scenario) {
super(scenario);
}
#Parameterized.Parameters
public static Iterable<Scenario> scenarios() throws IOException {
return new GreenCoffeeConfig()
.withFeatureFromAssets("assets/members.feature")
.scenarios();
}
#Test
public void test() {
start(new memberSteps());
}
}
And my members.feature source:
Feature: Inserting info to server
Scenario: Invalid members
When I introduce an invalid members
And I press the login button
Then I see an error message saying 'Invalid members'
Regarding the questions about the constructors. Due to the fact that tests in GreenCoffee require:
#RunWith(Parameterized.class)
The static method annotated with #Parameters must return a list of something (but not necessarily Scenario). The examples in the documentation simply return a list of scenarios, that's why the constructor must take a single Scenario as a parameter.
However, you can create a class that encapsulates the scenario and other objects that you may need to pass to the constructor. For example, given the following class:
public class TestParameters
{
public final String name;
public final Scenario scenario;
public TestParameters(String name, Scenario scenario)
{
this.name = name;
this.scenario = scenario;
}
}
You can write:
public TestConstructor(TestParameters testParameters)
{
super(testParameters.scenario);
}
#Parameters
public static Iterable<TestParameters> parameters() throws IOException
{
List<TestParameters> testParametersList = new ArrayList<>();
List<Scenario> scenarios = new GreenCoffeeConfig()
.withFeatureFromAssets("...")
.scenarios();
for (Scenario scenario : scenarios)
{
testParametersList.add(new TestParameters(scenario.name(), scenario));
}
return testParametersList;
}
In this way you can receive multiple values (encapsulated in an object) in the test constructor.
Solved problem by just fixing the structure.
code details in this commit
So I am looking at the mocking objects in a test.
However the following test will return npe when " Mockito.when(mock.getName()" portion of code is executed, thats because the mock object is returned as null.
package com.example.activity;
import com.example.BuildConfig;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertTrue;
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
#PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
#PrepareForTest(Static.class)
public class DeckardActivityTest {
#Rule
public PowerMockRule rule = new PowerMockRule();
#Mock
public ToBeMocked mock;
#Test
public void testSomething() throws Exception {
assertTrue(Robolectric.buildActivity(DeckardActivity.class).create().get() != null);
}
#Test
public void testStaticMocking() {
PowerMockito.mockStatic(Static.class);
Mockito.when(Static.staticMethod()).thenReturn(mock);
Mockito.when(mock.getName()).thenReturn("Mock");
assertTrue(Static.getResp().equals("Mock"));
}
}
and the simple class we are testing
package com.example.activity;
public class Static {
private static ToBeMocked toBeMocked;
public static ToBeMocked staticMethod() {
toBeMocked = new ToBeMocked();
return toBeMocked;
}
public static String getResp(){
return toBeMocked.getName();
}
}
So my understanding must be wrong.
What I would like to do is mock out the ToBeMocked class dependency give it and tell the mock object how to respond when getName method is called on it.
Can some one help me as to why this is going wrong or point out what I must have misunderstood
Yes, you understand it wrong. I mean you have misunderstanding how mocks work.
Your method getResp (in example) uses internal states which will not be set at all, because after you call PowerMockito.mockStatic(Static.class); all calls of static methods of the Static.class will be intercepted. So the code:
toBeMocked = new ToBeMocked();
return toBeMocked;
never is called.
If the getResp uses the staticMethod() then you code will work.
public static String getResp(){
return staticMethod().getName();
}
So you have two option to resolve your issue:
refactor your code as I pointed
use mock constructor to mock toBeMocked = new ToBeMocked();
I need to mock some custom class (create for it a shadow).
I have already read on http://robolectric.org/custom-shadows/ how to do this.
so, i have some class:
public class MyClass {
public static int regularMethod() { return 1; }
}
I create a shadow:
#Implements(MyClass.class)
public class MyShadowClass {
#Implementation
public static int regularMethod() { return 2; }
}
And i set the shadow in Test-class:
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, shadows={MyShadowClass.class})
public class MyTest {
#Test
public void testShadow() {
assertEquals(2, MyClass.regularMethod());
}
}
But the shadow is not used.
java.lang.AssertionError:
Expected :2
Actual :1
How to make any custom shadow visible for RobolectricGradleTestRunner?
I have already tried:
http://www.codinguser.com/2015/06/how-to-create-shadow-classes-in-robolectric-3/
https://github.com/jiahaoliuliu/RobolectricSample/blob/master/app-tests/src/main/java/com/jiahaoliuliu/robolectricsample/RobolectricGradleTestRunner.java
Mock native method with a Robolectric Custom shadow class
but i get various compilation errors, such as
InstrumentingClassLoaderConfig not found
Setup not found
how to use custom shadows correctly in robolectric 3.0?
Custom shadows should be avoided and must be a last ditch resort. It should only be used if you cannot do much refactor in your code which is preventing you from running your tests like a native method call. It's better to mock the object of that class or spy using powermock or mockito than custom shadow it. If it's a static method, then use powermock.
In our project, we had a class which had some native methods and it was the config class used everywhere in the app. So we moved the native methods to another class and shadowed that. Those native methods were failing the test cases.
Anyways here's how you can custom shadow in robolectric 3.0:
Create a custom test runner that extends RobolectricGradleTestRunner:
public class CustomRobolectricTestRunner extends RobolectricGradleTestRunner {
public CustomRobolectricTestRunner(Class<?> klass) throws InitializationError {
super(klass);
}
public InstrumentationConfiguration createClassLoaderConfig() {
InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder();
builder.addInstrumentedPackage("com.yourClassPackage");
return builder.build();
}
Make sure that that the package doesn't contain any test cases that you are running using robolectric.
I am Jiahao, the creator of the second repository that you are referring.
First of all thanks for to check my code. I do many researches on Android and I am glad that my research is useful for someone else.
Then, the Shadow about Robolectric. I am using Robolectric 3.1 in this project, to test how Robolectric 3 works with MarshMallow:
https://github.com/jiahaoliuliu/robolectricForMarshmallow
I have been testing the new Runtime Permission Manager, as well as shadowing application and activities.
Here is sample code of the shadowed activity:
import android.content.Context;
import com.jiahaoliuliu.robolectricformarshmallow.controller.MainController;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
/**
* Created by Jiahao on 7/18/16.
*/
#Implements(MainController.class)
public class MainControllerShadow {
public void __constructor__ (Context context) {
// Not do anything
}
#Implementation
public String getTextToDisplay(boolean permissionGranted) {
return "Test";
}
}
https://github.com/jiahaoliuliu/robolectricForMarshmallow/blob/master/app/src/test/java/com/jiahaoliuliu/robolectricformarshmallow/shadow/MainControllerShadow.java
And this is how I am using it in the unit test:
package com.jiahaoliuliu.robolectricformarshmallow;
import com.jiahaoliuliu.robolectricformarshmallow.shadow.MainControllerShadow;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.*;
/**
* Created by Jiahao on 6/30/16.
*/
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, manifest = Config.NONE, application = FoolApplication.class,
shadows = { MainControllerShadow.class}, sdk = 18)
public class MainActivityTest {
private MainActivity mMainActivity;
#Before
public void setUp() throws Exception {
mMainActivity = Robolectric.setupActivity(MainActivity.class);
}
#After
public void tearDown() throws Exception {
}
#Test
public void testOnCreate() throws Exception {
// Simple test to know that it works
assertTrue(true);
}
}
https://github.com/jiahaoliuliu/robolectricForMarshmallow/blob/master/app/src/test/java/com/jiahaoliuliu/robolectricformarshmallow/MainActivityTest.java
As you can see, I am not using customized Gradle Test Runner. I have checked the source code of Robolectric, for version 3.0 and 3.1 (latest) it is good enough to just specify the shadow classes in the header.
I hope it helps
I'm trying to authenticate with a REST API via a token using AndroidAnnotations #Rest RestClient Interface.
But due to a lack of documentation I can't get it running using setBearerAuth(String token);
I already have the interface and the class HeadersRequestInterceptor implements ClientHttpRequestInterceptor, but I don't know where to call setBearerAuth(myCustomToken);
If someone could give me a hint I would be very graceful.
Cheers
MyRestClient.java:
#RequiresAuthentication
#Rest(rootUrl = "your_url", converters = {...})
public interface MyRestClient extends RestClientHeaders {
#Post("/somecall")
public void someApiCall();
}
MyActivity.java:
#EActivity
public class MyActivity extends Activity {
#RestService
MyRestClient client;
#Background
void callSomeApi() {
String accessToken = ... ; // was previously returned from server
client.setBearerAuth(accessToken);
client.someApiCall(); // it will add header: Authorization: Bearer accessToken
}
}
I found out a better way, let-me explain:
If you need to apply that in all your Android REST calling, so we need to create a Interceptor to get all REST request made by the application like this:
import android.content.Context;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.androidannotations.rest.spring.annotations.Rest;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
/**
* Created by Rafael Rocha on 21/05/2018.
*/
#EBean
public class HttpBasicAuthenticatorInterceptor implements ClientHttpRequestInterceptor {
#RootContext
Context context;
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add("Authorization",AUTHENTICATION_SCHEME + "your_token_goes_here");
return execution.execute(request, body);
}
}
Replace AUTHENTICATION_SCHEME by your authentication method, in my case was "Bearer" and don't forget to put a blank space between them "Bearer YOUR_TOKEN".
After that is just put a reference in your REST service class on #Rest annotation filling the property "interceptors"
import org.androidannotations.rest.spring.annotations.Accept;
import org.androidannotations.rest.spring.annotations.Get;
import org.androidannotations.rest.spring.annotations.Header;
import org.androidannotations.rest.spring.annotations.Path;
import org.androidannotations.rest.spring.annotations.Rest;
import org.androidannotations.rest.spring.api.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.util.List;
/**
* Created by Rafael Rocha on 13/03/2018.
*/
#Rest(rootUrl = RestUtil.ROOT_URL, converters = {MappingJackson2HttpMessageConverter.class}, interceptors = {HttpBasicAuthenticatorInterceptor.class})
#Accept(MediaType.APPLICATION_JSON)
public interface NotificationRest {
//#Header(name = "server_version", value = SERVER_VERSION)
#Get("notificacao/profissional/recentes/{idProfessional}")
List<ResponseNotification> getNotifications(#Path Long idProfessional);
}
The only thing I did was put a Header value in the request header. You can do it to pass other things for example here in my application a need to pass my application android version to server side treats the old application or simply block the access, so I just need to increase on more line in the code
request.getHeaders().add(RestUtil.SERVER_VERSION_HEADER,RestUtil.SERVER_VERSION);
I am trying to write a backend service in Android, which reads a remote database and executes a query on it, returning the result in a ResultSet object. This is the code for my Java Bean which I return from the API call (called SQLResult):
package com.gradai.rushhour.backend;
import java.sql.ResultSet;
public class SQLResult
{
ResultSet result;
public SQLResult()
{
}
public ResultSet getResult()
{
return result;
}
public void setResult(ResultSet rs)
{
result = rs;
}
}
Now I try to use this API (called sqlBackend) in an AsyncTask in the actual app. In the onPostExecute function, I am getting an error:
error: incompatible types: com.gradai.rushhour.backend.sqlBackend.model.ResultSet cannot be converted to java.sql.ResultSet
This is the code for the said function:
protected void onPostExecute(SQLResult result)
{
ResultSet rs = result.getResult();
Toast toast = Toast.makeText(context, rs.toString(), Toast.LENGTH_LONG);
toast.show();
}
I don't get why is a ResultSet class being created in the backend model. Please help me in understanding what is going on. I'll be glad to provide further code if needed for debugging.
EDIT: These are the import statements used in the AsyncTask program:
import android.content.Context;
import android.os.AsyncTask;
import android.widget.Toast;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.json.gson.GsonFactory;
import com.gradai.rushhour.backend.sqlBackend.SqlBackend;
import com.gradai.rushhour.backend.sqlBackend.model.SQLResult;
import java.io.IOException;
import java.sql.ResultSet;
Also, please note that: I tried to convert the ResultSet in the API function to an ArrayList, and changed the SQLResult class to contain the same. Now, when I try to access the ArrayList in the same function, I get the error that a List is expected instead of an ArrayList. Could that be helpful?