How to run UnitTest Android service, which depends on network connection

I have an Android Service that handles communication with a game sync server, sending messages over TCP back and forth.

I would like to be able to unit test the behavior of this service. Namely, when the data is visible on the wire, that the data is read, analyzed and sent, the actual corresponding intention, and when the Service receives the intention, it correctly creates a message that should be sent to the server.

I'm not very good at unit testing, but I'm trying to make part of the testing part of my practice. I'm not sure how to approach something like this. It seems to me that somehow I will need a socket layout and fake input and output streams, but I really do not know how to do this, especially with regard to Android.

Here is the service (greatly reduced for peers):

public class GameSyncService extends Service { Thread mInputThread = new Thread() { /** * Parse commands from a message string, broadcast the command intents, and * return the remainder of the message * @param message The message to parse for commands * @returns the remaining characters */ private String parseCommands(String message) { // Parse the command, Broadcast the Intent and return any remainder } @Override public void run() { String message = ""; int charsRead = 0; char [] buffer = new char[BUFFER_SIZE]; while(!Thread.interrupted()) { try { while ((charsRead = mIn.read(buffer)) != -1) { message += new String(buffer).substring(0, charsRead); message = parseCommands(message); } } catch (IOException e) { Log.d(LOG_TAG, "Error receiving response: " + e.getLocalizedMessage()); disconnectFromServer(); connectToServer(); } } } }; private BroadcastReceiver mMessageSender = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String message = intent.getStringExtra("message"); sendMessage(message); } }; @Override public IBinder onBind(Intent arg0) { return null; } private void sendMessage(String message) { new SendCommandMessageTask().execute(message); } /** * Create a new connection to the server */ private void connectToServer() { try { if (mSocket == null) { mSocket = new Socket(mHost, mPort); mOut = new PrintWriter(mSocket.getOutputStream()); mIn = new BufferedReader(new InputStreamReader(mSocket.getInputStream()), BUFFER_SIZE); sendMessage("Handshake:|" + pInfo.versionName); } } catch (IOException e) { e.printStackTrace(); } } /** * Disconnect from the server and reset the socket to null */ private void disconnectFromServer() { if (mSocket != null) { try { mIn.close(); mOut.close(); mSocket.close(); mSocket = null; } catch (IOException e) { e.printStackTrace(); } } } @Override public int onStartCommand(Intent i, int flags, int startId) { Log.d(LOG_TAG, "GameSyncService Started"); mHost = i.getStringExtra("host"); mPort = i.getIntExtra("port", 9000); connectToServer(); mInputThread.start(); return START_STICKY; } @Override public void onCreate() { registerReceiver(mMessageSender, new IntentFilter(COMMAND_MESSAGE_SEND_ACTION)); try { pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); } catch (NameNotFoundException e) { e.printStackTrace(); } super.onCreate(); } @Override public void onDestroy() { unregisterReceiver(mMessageSender); super.onDestroy(); } } 
+4
source share
2 answers

Welcome to the world of ridicule. What you need to do can be easily done using the Android Mock . You should read about how Expectations works with Android Mock in a Writing Tests project using Android Mock .

What I would do is implement a Socket service that encapsulates TCP / socket calls. Then, using Android Mock, you mock your Socket service and use Expectations to verify the correctness of the data transmitted by a higher-level method (for example, GameSyncService).

+2
source

I found this problem in my own project, I usually try to introduce a real implementation (in this case, your Socket or what you want to mock) into the configuration class or the mock class.

I also found the Roboguice library useful , which helps a lot to inject instances of a particular class into this configuration class.

Injecting your Socket class will jump to your configuration class and create an instance of the constructor that you defined.

@Inject Socket socket; //on your game class

bind(Socket.class).toInstance(socketImplentation); //on your Application class

with this, you can do an Android testing project, which can determine in your @SetUp that you will use the mock implementation for the socket in this test or the mock message in another, etc.

Hope this helps with the idea :)

+1
source

Source: https://habr.com/ru/post/1411716/


All Articles