I'm trying to carry out junit test for the Android-DDP library.
To initialize the meteor object, we need a reference to a android context which I'm able to achieve using Robolectric. But the web-sockets is probably talking to the server on a different thread because of which the callback methods are not called and the test methods are getting end.
I used netstat to check if the android client is trying to communicate or not. It shows various ping/pong messages. So, Yes it is trying to talk to the server.
I went through this tutorial as well,
Android AsyncTask testing with Android Test Framework. This one tells how to handle the network on UI thread. But nothing seems right.
The sample code, I have worked is:
#Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
#RunWith(RobolectricGradleTestRunner.class)
public class MainActivityTest {
private MainActivity activity;
private Meteor meteor;
private String globalUrl = "ws://10.0.3.222:3000/websocket";
#Before
public void setup() {
activity = Robolectric.setupActivity(MainActivity.class);
meteor = new Meteor(activity, globalUrl);
meteor.reconnect();
/*
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
}
#Test
public void validateMeteorIsConnected() {
assertTrue(meteor.isConnected());
}
}
Any help would be appreciable. Thanks in advance.
You defined two methods, setup() and validateMeteorIsConnected(), but where are they called?
First, your setup is not correct. After your call to new Meteor(...), you don't need the reconnect() call because the constructor does already establish the connection.
Moreover, you must set up a listener so that you know when the connection has been established or data comes in. This is done with mMeteor.setCallback(...); where the parameter is this or activity.
As you said, the work is done on a different thread and everything is asynchronous.
So you can't just call validateMeteorIsConnected() immediately after connecting.
You need some timer, as shown in the question that you linked to.
Related
Hello I have the following project structure
--App
|--SDK1
|--SDK2
In app I have some test for check SDK1 and SDK2.
In SDK I have a singleton pattern only to set the context by the application class that is in App.
And the context is set in the SDK1.singleton in the App.Application.onCreate
The problem is that when I try to execute the following code I always get null:
#RunWith(RobolectricTestRunner.class)
public class CallTest {
#Before
public void setUp() throws Exception {}
#Test
public void connectToSocketTest() {
if (BuildConfig.FLAVOR.equals("dev")) {
Context context = SDK1.getInstance().getContext();
assertNotNull(context);
...
Any idea why this happens, and how can solve it?
That's not the recommended way to get app context in Android using robolectric. You can get your activity by using below code...
Activity activity = Robolectric.setupActivity(MyActivity.class);
To get the app context, just call activity.getApplicationContext().
EDIT1: If you're using the latest Robolectric version, use
Robolectric.buildActivity(DashboardActivity.class) instead.
EDIT2: Make sure your SDK1 extends MultiDexApplication". Add this #Config(manifest=Config.NONE, application = App.class, sdk = 17)` to
the top of your test class.
Let me know if it works.
I have a below scenario when setView is called then Presenter fetches some data over the network on a new thread. Test fails by giving this reason - Actually, there were zero interactions with this mock. But it should pass if interaction gets verified.
Testcase
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
verify(rateView,times(1)).showRate(new Rate());
}
Inside "ratePresenter.setView"
Call<UnoRate> call1 = ratesAPI.getUnoRate();
call1.enqueue(new Callback<UnoRate>() {
#Override
public void onResponse(Call<UnoRate> call,Response<UnoRate> response) {
UnoRate unoRate = response.body();
Rate rate = new Rate();
rate.setBuyRate(unoRate.getBuy());
rate.setSellRate(unoRate.getSell());
rate.setFee(0);
rateView.showRate(rate);
}
});
One very simple solution is to use Mockito's verification with timeout feature. This will retry the verification repeatedly up until the timeout, looking for the condition to pass at some point or another.
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
verify(rateView, timeout(100).times(1)).showRate(new Rate());
}
The docs, however, warn against it: "This feature should be used rarely - figure out a better way of testing your multi-threaded system." This is probably because you're introducing a new aspect--time--as a proxy for the thing you really want to check, which is that all of the queues have been processed. You could even imagine a busy enough VM where a conservative timeout could cause the test to flake in automated testing systems but that works fine on development machines.
If feasible, you could switch your ratesAPI to use a synchronous executor, or instead you could add methods needed to your API accessor to block the test thread until all calls have returned asynchronously:
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
ratesAPI.flush(); // Implement this to perform a Thread.join on the callback thread,
// or otherwise wait until all callbacks have been called.
verify(rateView,times(1)).showRate(new Rate());
}
Or, to remove multithreading and external API interactions from your test, simulate the callback synchronously:
#Mock RatesAPI ratesApiMock;
#Mock Call<UnoRate> unoRateCallMock;
#Captor Callback<UnoRate> unoRateCallbackCaptor;
#Test
public void checkUnoRate() {
// Set up mock.
when(ratesApiMock.getUnoRate()).thenReturn(unoRateCallMock);
// Perform the action.
ratePresenter.setView(rateView,Constants.UNO);
// Verify nothing happens yet.
verify(rateView, never()).showRate(any());
// Capture and trigger the callback.
verify(unoRateCallMock).enqueue(unoRateCallbackCaptor.capture());
unoRateCallbackCaptor.getValue().onResponse(yourCall, yourResponse);
// Verify the asynchronous action.
verify(rateView,times(1)).showRate(new Rate());
}
As a side note, eventually you'll probably want to verify against a different parameter than new Rate(). Mockito compares via equals methods when not using Mockito matchers.
I have a problem with jsoup on android. I have seen the other posts and tried solutions that were suggested there ( re-adding the jars, calling android fix tool, etc.)
I have added the jsoup jar to my android project (using build path), and added the required
internet permission to my manifest.
<uses-permission android:name="android.permission.INTERNET" />
but when I am trying to run my application I am getting
Could not find method org.jsoup.Jsoup.connect, referenced from method com.example.test.MainActivity.onCreate
I have tried to use the android fix tool but it did not solve the problem.
All I have is a main activity and I am trying to call
Document doc = Jsoup.connect("http://en.wikipedia.org/").get();
attached is part of my code
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Document doc = Jsoup.connect("http://en.wikipedia.org/").get();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
You are trying to run your connection in your main thread. Use AsyncTask and it will work.
I.E.
public class JsoupParser extends AsyncTask...
Why you have to use AsyncTask for network connections in android?
AsyncTask is an abstract helper class that enables you to use the UI thread correctly, while performing background operations in a different thread, without having to really handle threads or controllers. Since android is implemented using a single thread model, each time you launch an application, a new thread will be created.
Imagine you have a single thread model where you at a button click will parse a website using Jsoup. This would have worked fine in earler android versions, though you would have had a non-responsive screen until the network operation is done. The AsyncTask will run in the background enabling your screen to still be responsive while another thread takes care of the network communication.
Take a look in the API:
AsyncTask
NetworkOnMainThreadException
Delete all statements like:
System.out.println(something);
It worked for me, realizing this took me 2 hours.
In you normal activity
use this
public static int SDK_INT = android.os.Build.VERSION.SDK_INT;
and before fetching Document
write this inside try block
if (SDK_INT >= 10) {
ThreadPolicy tp = ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);
}
it worked for me
I am developing an Android app for connecting to Tridion 2011 SP1 Core Service.
So far I have created Android WS Stubs from the core service wsdl using wsclient.
Imported those stubs, which allow access to all the core service methods.
I can now authenticate to Tridion via the Android application but as soon as I try to perform even the most basic of web service calls, such as getApiVersion(), I get the error:
ReflectionHelper*java.lang.NoSuchFieldException: GetApiVersionResult.
I was wondering has anyone else managed to create a java android app that communicates with the Core Service?
Interestingly enough, if I run the code as a java application, using wsimport stubs everything works a treat.
Any help appreciated. For reference here is a code snippet:
To connect to Tridion:
class TridionConnect extends AsyncTask<String, Integer, String> {
// Called to initiate the background activity
#Override
protected String doInBackground(String... statuses) {
try {
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username", "password".toCharArray());
}
});
url = new URL("http://tridion-server/webservices/CoreService2011.svc?wsdl");
System.out.println(String.format("Get Service"));
service = new CoreService2011();
System.out.println(String.format("Get Client"));
client = service.getBasicHttp();
return "Authenticated To Tridion";
} catch (Exception e) {
Log.e("Authentication failure", e.toString());
e.printStackTrace();
return "Failed to authenticate";
}
}
// Called when there's a status to be updated
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// Not used in this case
}
// Called once the background activity has completed
#Override
protected void onPostExecute(String result) { //
Toast.makeText(FullscreenActivity.this, result, Toast.LENGTH_LONG).show();
area.setText("Authenticated to Tridion OK");
}
}
To get the ApiVersion
client.getApiVersion();
UserData currentUser = client.getCurrentUser();
System.out.println(String.format("'%s' %s", currentUser.getTitle(), currentUser.getId()));
Frank,
It is not possible for a couple of reasons.
If you use wsimport to create the coreservice proxy it will use the javax library, which exists in the JRE. However Dalvik implements only a subset of the javax library which means this approach is impossible in the Android environment.
I then looked at Ksoap2 tools for creating the proxy. This seemed to work OK, in as much as it did create a proxy, however it did not match the coreservice so I was unable to authenticate. I didn't get any further with this approach beyond examining the JRE proxy v Ksoap2 proxy. They were quite different.
At this point I took a step back, had a cup of tea and re-engineered the approach.
I created a c# REST service to sit between the android app and the core service.
This approach seemed a bit complex, but it offers lots of advantages. Lots of the spade work can be done in the REST service, which will be much quicker than similar code on a tablet or phone.
Secondly the REST service sits on the same server as the CMS/CoreService so the comms is quicker and you can make the REST requests from the android app much lighter.
I have got the application to the point where you can authenticate to Tridion, select a publication, and components that is then rendered in a dynamic layout ready for update/save/publish.
The one big downside of this approach is that the REST service 'should' be stateless so superficially you have to authenticate to the coreservice for every request. Of course I don't do that, but you have to come up with some alternative approach Oauth, shared secret etc.
In initial tests this approach has seemed to be fairly slick on an android device.
after each test execution junit goes to the next test. And activity closes automatically after each test execution.
Its OK when I test functions, but NOT OK when I test views. I want to see execution results on the screen before junit will execute next test.
So, I want to make small delay between tests.
There is one way I can do it:
public void testMyView() throws InterruptedException {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
// viewOperations...
}
});
Thread.sleep(3000);
}
But I dont like this way because this code is dirty (especially when I have a lot of such tests).
Another way seems much better:
#UiThreadTest
public void testHandleEmptyAlphabetList() throws InterruptedException {
// view operations
Thread.sleep(3000);
}
BUT: It will block UI thread but NOT test thread. I need to block a test thread.
Is there any way to run Thread.sleep(3000); from this code separately in a test thread?
So, what is the best way to make thread delays between tests?
p.s. Please don't recommend me additional test frameworks, I want to solve my problem using embed tools. Thank you.
I suggest ditching this cumbersome way of unit testing and using Robolectric, so your tests run in the JVM and you don't have to deal with all this nasty setup. You'll be pleasantly surprised on how quickly your View testing runs, and that you don't need to run an emulator!
Try to use Robotium for the tests that need a real android running.
solo = new Solo(getInstrumentation(), getActivity());
[...]
if (failure) {
solo.takeScreenshot();
}
This will save a screenshot to your SD card (needs WRITE_EXTERNAL_STORAGE permission)
Or if you prefer not to use another testing framework (though I would recommend you take a look at it), just steal the code that is used there for taking screenshots