I've got an application that performs HTTP GET calls using HttpGet and I would like to mock the response in order to test different scenarios without having to setup any specific local server that would act like the remote one.
The goal is to have very high level tests that acts like a real user (Robotium) and fake the response that the application would obtain calling the real server. Much like testing a Twitter client, if you need an example.
Ok, so this is what I did to get fake HttpResponses in my Robotium tests:
- I have a class HttpCallBuilder, that usually just returns a DefaultHttpClient
- I added a setHttpClient() method to set a MockHttpClient in my tests (you need to implement (empty) a lot of methods in the HttpClient interface, which I omitted here):
public class MockHttpClient implements HttpClient {
private static Context context;
private final BasicHttpParams params = new BasicHttpParams();
#Override
public HttpResponse execute(HttpUriRequest request) throws IOException,
ClientProtocolException {
InputStream mockInputStream = context.getAssets().open(
MockResponses.forRequest(request));
return new MockHttpResponse(mockInputStream);
}
#Override
public HttpParams getParams() {
return params;
}
public static void setContext(Context applicationContext) {
MockHttpClient.context = applicationContext;
}
}
MockResponses allows you to prime your Mock with the right responses for the situation:
public class MockResponses {
private static final List<String[]> responseMapping = new ArrayList<String[]>();
private static final String BASE = "mocks/";
public static String forRequest(final HttpUriRequest request) {
final String requestString = request.getURI().toString();
for (final String[] mapping : responseMapping) {
if (requestString.matches(mapping[0])) {
return BASE + mapping[1];
}
}
throw new IllegalArgumentException(
"No mocked reply configured for request: " + requestString);
}
public static void forRequestDoAnswer(final String regex,
final String fileToReturn) {
responseMapping.add(new String[] { regex, fileToReturn });
}
public static void reset() {
responseMapping.clear();
}
}
In your test you can then prepare your test like this:
HttpCallBuilder.setHttpClient(new MockHttpClient());
MockHttpClient.setContext(context);
MockResponses.reset();
MockResponses.forRequestDoAnswer(".*method=Login.*", "loginform.html");
Google provides a library named as Mockwebserver which can be used for mocking web service response. https://code.google.com/p/mockwebserver/ You can refer this link
How about using Mockito ?
According to this article its latest version should support dalvik, so you should be able to use it with robotium.
With mockito you can mock any object to return whatever you want. I found it very powerful and concise.
Try XML Mimic, that will solve your problem. It is easy to configure and runs as independent server.
http://sourceforge.net/projects/xmlmimic/
Related
I need a working example for a custom API for Microsoft Azure App Service.
I could not get any useful or working information/examples for that, or they just show each time different approaches which are outdated?!?!
For now I have a working table controller which gets information from database and returns it back to my Android client. Now I need to define a custom API Controller to get a string back. In the examples they are all sending an object to the service in order to get an object back. I do not want to send anything to the API, just retrieve some information back from a GET Request.
Regards
// EDIT - Added / edited client / server code to Post a String.
You can use the following code to do a GET request on the auto generated API controller Visual Studio creates (ValuesController).
private void getStringFromAzure() throws MalformedURLException {
// Create the MobileService Client object and set your backend URL
String yourURL = "https://yourApp.azurewebsites.net/";
MobileServiceClient mClient = new MobileServiceClient(yourURL, this);
// Your query pointing to yourURL/api/values
ListenableFuture<JsonElement> query = mClient.invokeApi("values", null, GetMethod, null);
// Callback method
Futures.addCallback(query, new FutureCallback<JsonElement>() {
#Override
public void onSuccess(JsonElement jsonElement) {
// You are expecting a String you can just output the result.
final String result = jsonElement.toString();
// Since you are on a async task, you need to show the result on the UI thread
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(mContext, result, Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onFailure(Throwable throwable) {
Log.d(TAG, "onFailure: " + throwable.getMessage());
}
});
}
public void sendString(final String someString) throws MalformedURLException {
// Your query pointing to /api/values/{String}
ListenableFuture<JsonElement> query = mClient.invokeApi("values/" + someString, null, PostMethod, null);
// Callback method
Futures.addCallback(query, new FutureCallback<JsonElement>() {
#Override
public void onSuccess(JsonElement jsonElement) {
// You are expecting a String you can just output the result.
final String result = jsonElement.toString();
}
#Override
public void onFailure(Throwable throwable) { }
});
}
The backend API: (ValuesController)
{
// Use the MobileAppController attribute for each ApiController you want to use
// from your mobile clients
[MobileAppController]
public class ValuesController : ApiController
{
// GET api/values
public string Get()
{
return "Hello World!";
}
// POST api/values/inputString
public string Post(string inputString)
{
return inputString;
}
}
}
You can also send parameters along in the following way:
List<Pair<String, String>> parameters = new ArrayList<>();
parameters.add(new Pair<>("name", "John"));
parameters.add(new Pair<>("password", "fourwordsalluppercase"));
ListenableFuture<JsonElement> query = client.invokeApi("yourAPI", PostMethod, parameters);
Or as json in the body:
JsonObject body = new JsonObject();
body.addProperty("currentPassword", currentPassword);
body.addProperty("password", password);
body.addProperty("confirmPassword", confirmPassword);
ListenableFuture<JsonElement> query = mClient.invokeApi("yourAPI", body, PostMethod, null);
Based on my understanding, I think there are two parts in your question which include as below. And I think you can separately refer to two sections to get the answers and write your own example.
How to define a custom API on Azure Mobile App to retrieve data from database? Please refer to the section Custom APIs to know how to do with Azure Mobile App backend.
How to call a custom API from Android App? Please refer to the section How to: Call a custom API to know how to do with Android SDK.
I have a simple server rest endpoint running Spring -
#RestController
#RequestMapping("/services")
#Transactional
public class CustomerSignInService {
#Autowired
private CustomerDAO customerDao;
#RequestMapping("/customer/signin")
public Customer customerSignIn(#RequestParam(value = "customer") Customer customer) {
//Some Code Here...
return customer;
}
}
I'm trying to pass a Customer object from my Xamarin Android App using this method -
public JsonValue send(String url, SmartJsonSerializer obj)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
request.ContentType = "application/json";
request.Method = "POST";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(obj.toJsonString());
}
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
return JsonObject.Load(stream);
}
}
}
But i keep getting Bad Request Exception (Http Error 400) and obviously my code at the server side is not triggered.
SmartJsonSerializer uses JSON.NET to serialize the Customer object to string -
using System;
using Newtonsoft.Json;
namespace Shared
{
public class SmartJsonSerializer
{
public string toJson()
{
return JsonConvert.SerializeObject(this);
}
}
}
Any help appreciated,
thnx!
Typically if you are posting a complex object to an api like this, you would write it in the request body. You do appear to be doing this on the android side.
I am not familiar with Spring, but it looks that you are expecting customer as a url parameter - Try replacing #RequestParam with #RequestBody.
I was struggling with it for a while, but apparently the solution may be find in the server side.
If it helps, You can look at this
#RestController
#RequestMapping("/services")
#Transactional
public class SomeService {
#RequestMapping(value = "/user/signin", method = RequestMethod.POST)
#ResponseBody
public AppUser signIn(#RequestBody AppUser appUser) {
appUser.invoke();
return appUser;
}
}
i did not really like to take your time for my problem, but after 1 week of searching about registration code of Asmack, i ended up with try/fail on the clues,because there is extacly non simple of that on net, here is my code:
public class Registration extends IQ {
public static final String HOST = "http://127.0.0.1";
public static final int PORT = 9090;
public static final String SERVICE = "what is this?!";
public static final String USERNAME = "reza";
public static final String PASSWORD = "mypassword";
XMPPConnection connection;
public void create() {
ConnectionConfiguration connConfig =
new ConnectionConfiguration(HOST,PORT, SERVICE);
connection = new XMPPConnection(connConfig);
connection.connect();
AccountManager am = new AccountManager(connection);
Map<String, String> mp = new HashMap<String, String>();
// adding or set elements in Map by put method key and value
// pair
mp.put("username", USERNAME);
mp.put("password", PASSWORD);
// am.createAccount(mConfig.userName, mConfig.password);
am.createAccount(USERNAME, PASSWORD, mp);
}
#Override
public CharSequence getChildElementXML() {
// TODO Auto-generated method stub
return null;
}
}
but it returns error in codding can not instantiate the type XMPPConnection and the constructor AccountManager is not visible, can you help me with the code, and also i have questions what is service in the connection configuration and what is the CharSequence getChildElementXML() for? thanks alot, if you could lead me to an android smack definitive guide, that would best best thing some one did for me in past 20years , also this could be a guide for any one else who searching to learn like me ;)
If you are using openfire on server side then you can use userservice plugin there.
Using this plugin you can hit http or https web service to register the user or deactivate the user.
I'm looking for a way to mock api responses in android tests.
I have read the roboelectric could be used for this but I would really appreciate any advice on this.
After a small bit of looking around on the web I have found MockWebServer to be what I was looking for.
A scriptable web server for testing HTTP clients. This library makes it easy to test that your app Does The Right Thing when it makes HTTP and HTTPS calls. It lets you specify which responses to return and then verify that requests were made as expected.
To get setup just add the following to your build.gradle file.
androidTestCompile 'com.google.mockwebserver:mockwebserver:20130706'
Here is a simple example taking from their GitHub page.
public void test() throws Exception {
// Create a MockWebServer. These are lean enough that you can create a new
// instance for every unit test.
MockWebServer server = new MockWebServer();
// Schedule some responses.
server.enqueue(new MockResponse().setBody("hello, world!"));
// Start the server.
server.play();
// Ask the server for its URL. You'll need this to make HTTP requests.
URL baseUrl = server.getUrl("/v1/chat/");
// Exercise your application code, which should make those HTTP requests.
// Responses are returned in the same order that they are enqueued.
Chat chat = new Chat(baseUrl);
chat.loadMore();
assertEquals("hello, world!", chat.messages());
// Shut down the server. Instances cannot be reused.
server.shutdown();
}
Hope this helps.
MockWebServer didn't work for me with AndroidTestCase. For instance, ECONNREFUSED error happened quite randomly (described in https://github.com/square/okhttp/issues/1069). I didn't try Robolectric.
As of OkHttp 2.2.0, I found an alternative way which worked well for me: Interceptors. I placed the whole mock response inside a json file stored on androidTest/assets/, say, 'mock_response.json'. When I instanced an OkHttp for testing, I exposed an Interceptor which I would rewrite the incoming response. Basically, body() would instead stream the data in 'mock_response.json'.
public class FooApiTest extends AndroidTestCase {
public void testFetchData() throws InterruptedException, IOException {
// mock_response.json is placed on 'androidTest/assets/'
final InputStream stream = getContext().getAssets().open("mock_response.json");
OkHttpClient httpClient = new OkHttpClient();
httpClient.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
return new Response.Builder()
.protocol(Protocol.HTTP_2)
// This is essential as it makes response.isSuccessful() returning true.
.code(200)
.request(chain.request())
.body(new ResponseBody() {
#Override
public MediaType contentType() {
return null;
}
#Override
public long contentLength() {
// Means we don't know the length beforehand.
return -1;
}
#Override
public BufferedSource source() {
try {
return new Buffer().readFrom(stream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
})
.build();
}
});
FooApi api = new FooApi(httpClient);
api.fetchData();
// TODO: Let's assert the data here.
}
}
This is now even easier with Mockinizer which makes working with MockWebServer easier:
val mocks: Map<RequestFilter, MockResponse> = mapOf(
RequestFilter("/mocked") to MockResponse().apply {
setResponseCode(200)
setBody("""{"title": "Banana Mock"}""")
},
RequestFilter("/mockedError") to MockResponse().apply {
setResponseCode(400)
}
)
Just create a map of RequestFilter and MockResponses and then plug it into your OkHttpClient builder chain:
OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.mockinize(mocks) // <-- just plug in your custom mocks here
.build()
It seems that I am unable to set arbitrary query parameters to a #Get declaration
My endpoint looks like
http://api.lmiforall.org.uk/api/v1/ashe/estimateHours?soc=2349&coarse=true
There are a non trivial amount of parameters to this query, is there a declaration I can use to indicate this to the #Rest interface?
I tried declaring it as this, but it complains about fields being unused.
#Get("estimateHours")
ASHEFilterInfo GetEstimateHours( int soc, boolean coarse, String filters, String breakdown);
java: #org.androidannotations.annotations.rest.Get annotated method has only url variables in the method parameters
Look at AA cookbook.
Try this (not tested):
#Rest(rootUrl = "http://api.lmiforall.org.uk/api/v1/ashe")
public interface MyService {
#Get("/estimateHours?soc={soc}&coarse={coarse}&breakdown={breakdonw}&filters={filters}")
ASHEFilterInfo GetEstimateHoursFiltered( int soc, boolean coarse, String filters, String breakdown);
#Get("/estimateHours?soc={soc}&coarse={coarse}&breakdown={breakdonw}")
ASHEFilterInfo GetEstimateHours( int soc, boolean coarse, String breakdown);
}
When I needed to create #Get request with many dynamic parameteres, and some of them could be duplicated, I had resolved that problem so:
#Rest(rootUrl = "http://example.com:9080/",
converters = { GsonHttpMessageConverter.class },
interceptors = { ApiInterceptor.class })
public interface ExampleApi {
#Get("content/home/product-type/list?{filters}&domain={domain}") //filters is String like "param1=value1¶m1=value2¶m3=value3"
ProductTypeListResponse getProductTypeList(int domain, String filters);
}
public class ApiInterceptor implements ClientHttpRequestInterceptor {
private static final String TAG = ApiInterceptor.class.getSimpleName();
#Override
public ClientHttpResponse intercept(final HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
final QueryMultiParamsHttpRequest modifiedRequest = new QueryMultiParamsHttpRequest(request);
return execution.execute(modifiedRequest, body);
}
}
public class QueryMultiParamsHttpRequest implements HttpRequest {
private static final String TAG = QueryParametersBuilder.class.getSimpleName();
private HttpRequest httpRequest;
public QueryMultiParamsHttpRequest(final HttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
#Override
public HttpMethod getMethod() {
return httpRequest.getMethod();
}
#Override
public URI getURI() {
final URI originalURI = httpRequest.getURI();
final String query = originalURI.getQuery() != null ? originalURI.getQuery().replace("%3D", "=").replace("%26", "&") : null;
URI newURI = null;
try {
newURI = new URI(originalURI.getScheme(), originalURI.getUserInfo(), originalURI.getHost(), originalURI.getPort(), originalURI.getPath(),
query, originalURI.getFragment());
} catch (URISyntaxException e) {
Log.e(TAG, "Error while creating URI of QueryMultiParamsHttpRequest", e);
}
return newURI;
}
#Override
public HttpHeaders getHeaders() {
return httpRequest.getHeaders();
}
}
So, I created a wrapper for HttpRequest, that can decode symbols "=" and "&". And this wrapper replaces original HttpRequest in ApiInterceptor. This is a little hacky solution, but it works.
I ran into this same issue and came up with a another solution that while far from ideal, works. The particular problem I was trying to solve was handling "HATEOAS" links.
What I ended up doing was creating a separate class called HATEOASClient to contain endpoint methods that would not escape the HATEOAS links passed in as params. To do that I basically just looked at an auto generated endpoint method and coped/tweaked the body in my implementation.
These methods use the same RestTemplate instance AndroidAnnotations sets up so you still get access to all the general setup you do on the RestTemplate.
For example:
public ResponseEntity<Foo> postFoo(Foo foo) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(RestHeader.AUTH_TOKEN_HEADER, getClient().getHeader(RestHeader.AUTH_TOKEN_HEADER));
httpHeaders.set(RestHeader.ACCEPT_LANGUAGE_HEADER, getClient().getHeader(RestHeader.ACCEPT_LANGUAGE_HEADER));
httpHeaders.setAuthorization(authentication);
HttpEntity<Foo> requestEntity = new HttpEntity<>(null, httpHeaders);
HashMap<String, Object> urlVariables = new HashMap<>();
urlVariables.put("link", foo.getLinks().getFooCreate().getHref());
URI expanded = new UriTemplate(getClient().getRootUrl().
concat(API_VERSION + "{link}")).expand(urlVariables);
final String url;
try {
url = URLDecoder.decode(expanded.toString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return getClient().getRestTemplate().
exchange(url, HttpMethod.POST, requestEntity, Foo.class, urlVariables);
}
If all parameters is required you can use #Path annotation.
#Rest(rootUrl = "http://api.lmiforall.org.uk/api/v1/ashe")
public interface MyService {
#Get("/estimateHours?soc={soc}&coarse={coarse}&breakdown={breakdown}&filters={filters}")
ASHEFilterInfo GetEstimateHours(#Path int soc, #Path boolean coarse, #Path String breakdown, #Path String filters);
}
If one of the parameters is optional, there isn't yet a solution that can you can easily pass parameters using Android Annotations. But anybody can contribute to better Android Annotations.
if you define the params for each method then you need to provide them in each request. I thought this was sort of over kill too so what I did was just make a generic get/post request in my api client then just manually enter the values, if you don't define the root url I suppose you could use the QueryStringBuilder class and build the uri that way.
#Rest(rootUrl = "https://path/to/api/", converters = { FormHttpMessageConverter.class,
GsonHttpMessageConverter.class, ByteArrayHttpMessageConverter.class })
public interface ApiClient {
#Get("{uri}")
JsonElement apiGet(String uri);
#Post("{uri}")
JsonObject apiPost(String uri,MultiValueMap data);
RestTemplate getRestTemplate();
void setRootUrl(String rootUrl);
void setRestTemplate(RestTemplate restTemplate);
}
Example usage
JsonElement resp = apiClient.apiGet("method/?random_param=1&another_param=test);
It's not as clean but can be dynamic