Micro blog about Answer to the Ultimate Question of Life, the Universe, and Everything.
  • Home
    • List all categories
    • Sitemap
  • Downloads
    • WebSphere
    • Hitachi902
    • Hospital
    • Kryptonite
    • OCR
    • APK
  • About me
    • Gallery
      • Italy2022
      • Côte d'Azur 2024
    • Curriculum vitae
      • Resume
      • Lebenslauf
    • Social networks
      • Facebook
      • Twitter
      • LinkedIn
      • Xing
      • GitHub
      • Google Maps
      • Sports tracker
    • Adventures planning
  1. You are here:  
  2. Home
  3. Android

Mock context

Details
Written by: Stanko Milosev
Category: Android
Published: 01 April 2023
Last Updated: 02 April 2023
Hits: 608
Just a shor reminder for my sef before I forget it, how to mock a context:
var context: Context = Mockito.mock(Context::class.java)

MockWebServer example

Details
Written by: Stanko Milosev
Category: Android
Published: 01 April 2023
Last Updated: 03 April 2023
Hits: 686
  • kotlin
Here I gave one example how to create Retrofit (OkHttp) post method.

Now one example how to unit test it with MockWebServer(GitHub).

First I have added gradle:

//MockWebServer
testImplementation('com.squareup.okhttp3:mockwebserver:5.0.0-alpha.11')
testImplementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.11")

//RetroFit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
My example I have to change a little bit to be testable.

Api service:

interface IPostSomethingApiService {
    @Headers("Content-Type: text/json")
    @POST("/api/UpdateCoordinates")
    fun postMethod(@Body value: String): Call<String>
}
In order to be able to test onResponse and onFailure asynchronous callbacks of Retrofit, I have introduced new interface:
interface IPostSomethingCallbacks {
    fun onSuccess(message: String)
    fun onFailure(message: String)
}
Class to accept post:
package com.example.mockwebserverexample

import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class PostSomething (var postSomethingApiService: IPostSomethingApiService, var postSomethingCallbacks: IPostSomethingCallbacks) {

    private lateinit var webApiRequest: Call<String>

    fun myHttpPost(value: String) {
        webApiRequest = postSomethingApiService.postMethod(value)

        webApiRequest.enqueue(object : Callback<String> {
            override fun onResponse(call: Call<String>, response: Response<String>) {
                if (!response.isSuccessful) {
                    postSomethingCallbacks.onSuccess(response.errorBody()!!.charStream().readText())
                }
            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                postSomethingCallbacks.onFailure(t.message.toString())
            }
        })

    }
}
Unit test:
package com.example.mockwebserverexample

import junit.framework.TestCase
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test

//MockWebServer
import okhttp3.OkHttpClient
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer

//Retrofit
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory

class RetrofitUnitTest {

    private lateinit var mockWebServer: MockWebServer
    private lateinit var postSomethingApiService: IPostSomethingApiService
    private lateinit var postSomething: PostSomething
    private lateinit var postSomethingCallbacks: PostSomethingCallbacks

    @Before
    fun setUp() {

        mockWebServer = MockWebServer()
        val client = OkHttpClient.Builder()
            .build()

        postSomethingApiService = Retrofit.Builder()
            .baseUrl(mockWebServer.url("/"))
            .client(client)
            .addConverterFactory(ScalarsConverterFactory.create())
            .build().create(IPostSomethingApiService::class.java)

        postSomethingCallbacks = PostSomethingCallbacks()
        postSomething = PostSomething(postSomethingApiService, postSomethingCallbacks)

        mockWebServer.start()
    }

    @After
    fun tearDown() {
        mockWebServer.shutdown()
    }

    @Test
    fun `check if 400 response results in an error state`() {
        val response = MockResponse()
            .setBody("400 error test")
            .setResponseCode(400)

        mockWebServer.enqueue(response)

        postSomething.myHttpPost("test")

        runBlocking {
            postSomethingApiService.postMethod("test 400")
        }

    }
}

class PostSomethingCallbacks: IPostSomethingCallbacks {

    override fun onSuccess(message: String) {
        TestCase.assertEquals("400 error test",message)
    }

    override fun onFailure(message: String) {
        TestCase.assertEquals("400 error test",message)
    }

}
Notice code:
postSomething = PostSomething(postSomethingApiService, postSomethingCallbacks)
And class where are my asserts:
class PostSomethingCallbacks: IPostSomethingCallbacks {
 
    override fun onSuccess(message: String) {
        TestCase.assertEquals("400 error test",message)
    }
 
    override fun onFailure(message: String) {
        TestCase.assertEquals("400 error test",message)
    }
 
}
Example download from here. Update: Here is maybe better solution without MockWebServer which gave me ChatGPT:
import com.nhaarman.mockitokotlin2.*
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MyCallbackTest {

    private lateinit var mockedCall: Call
    private lateinit var myCallback: MyCallback
    private lateinit var mockedResponse: Response
    private lateinit var mockedResponseBody: String

    @Before
    fun setup() {
        mockedCall = mock()
        myCallback = MyCallback()
        mockedResponse = mock()
        mockedResponseBody = "response body"
    }

    @Test
    fun `onResponse success`() {
        // Given
        whenever(mockedResponse.isSuccessful).thenReturn(true)
        whenever(mockedResponse.body()).thenReturn(mockedResponseBody)
        whenever(mockedResponse.code()).thenReturn(200)

        // When
        myCallback.onResponse(mockedCall, mockedResponse)

        // Then
        assertEquals(mockedResponseBody, myCallback.responseBody)
    }

    @Test
    fun `onResponse failure`() {
        // Given
        val errorResponseBodyString = "error response body"
        val errorCode = 404
        whenever(mockedResponse.isSuccessful).thenReturn(false)
        whenever(mockedResponse.code()).thenReturn(errorCode)
        whenever(mockedResponse.errorBody()?.string()).thenReturn(errorResponseBodyString)

        // When
        myCallback.onResponse(mockedCall, mockedResponse)

        // Then
        assertEquals(errorResponseBodyString, myCallback.errorBody)
        assertEquals(errorCode, myCallback.errorCode)
    }

    private inner class MyCallback : Callback {
        var responseBody: String? = null
        var errorBody: String? = null
        var errorCode: Int? = null

        override fun onResponse(call: Call, response: Response) {
            if (response.isSuccessful) {
                responseBody = response.body()
            } else {
                errorBody = response.errorBody()?.string()
                errorCode = response.code()
            }
        }

        override fun onFailure(call: Call, t: Throwable) {
            // Not needed for this test
        }
    }
}
Gradle:
    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

    // Optional -- Robolectric environment
    testImplementation 'androidx.test:core:1.6.0-alpha01'
    // Optional -- Mockito framework
    testImplementation 'org.mockito:mockito-core:5.2.0'
    // Optional -- mockito-kotlin
    testImplementation 'org.mockito.kotlin:mockito-kotlin:4.1.0'
    // Optional -- Mockk framework
    testImplementation 'io.mockk:mockk:1.13.4'
    testImplementation 'junit:junit:4.13.2'
    testImplementation('com.squareup.okhttp3:mockwebserver:5.0.0-alpha.11')
    testImplementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.11")

    // required if you want to use Mockito for Android tests
    androidTestImplementation 'org.mockito:mockito-android:5.2.0'

    implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.11'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'


    implementation 'com.google.android.gms:play-services-location:21.0.1'
    implementation 'com.google.code.gson:gson:2.10.1'

Example of making HTTP Post and Get request from Android Kotlin to Web Api .NET Core

Details
Written by: Stanko Milosev
Category: Android
Published: 26 March 2023
Last Updated: 26 March 2023
Hits: 827
  • core
  • kotlin
This example works only in Android Studio emulator and with accepting all certificates, and it is made with Retrofit library.

First example of Web Api in .NET Core:

using Microsoft.AspNetCore.Mvc;

namespace WebApi.Controllers;

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    [HttpGet]
    public string Get()
    {
        return "test";
    }

    [HttpGet("{id}")]
    public string Get(int id)
    {
        return "value";
    }

    [HttpPost]
    public string Post([FromBody] string value)
    {
        return $"Sent: {value}";
    }
}
Now example in Kotlin.

In \app\src\main\AndroidManifest.xml I have added internet permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

In \app\src\main\res\layout\activity_main.xml I have added the button:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button"
        android:onClick="postHttp"
        tools:layout_editor_absoluteX="166dp"
        tools:layout_editor_absoluteY="441dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
        />

</androidx.constraintlayout.widget.ConstraintLayout>
In \app\build.gradle I have added

implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
Code to trust all certificates taken from here.

val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
    override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        Log.i(MainActivity::class.simpleName, "checkClientTrusted")
    }

    override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        Log.i(MainActivity::class.simpleName, "checkServerTrusted")
    }

    override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
})
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())

// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory

// connect to server
val client = OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager).hostnameVerifier{ _, _ -> true }.build()
Next building Retrofit:
val retro = Retrofit.Builder()
    .baseUrl("https://10.0.2.2:7037")
    .client(client)
    .addConverterFactory(ScalarsConverterFactory.create())
    .build()

Here notice baseUrl: https://10.0.2.2:7037 - 10.0.2.2 is localhost for Android Studio emulator

Add WebApiService interface:

interface WebApiService {
    @Headers("Content-Type: text/json")
    @POST("/api/Values")
    fun postMethod(@Body value: String): Call<String>
}
Create request:
val service = retro.create(WebApiService::class.java)
val webApiRequest = service.postMethod("\"test\"")
Here notice "\"test\"" since I am trying to send a raw string, otherwise would be rejected.

Last but not least, onResponse and onFailure:

val alertDialogBuilder = AlertDialog.Builder(this@MainActivity)

webApiRequest.enqueue(object : Callback<String> {
    override fun onResponse(call: Call<String>, response: Response<String>) {

        if (!response.isSuccessful) {
            alertDialogBuilder.setMessage(response.errorBody()!!.charStream().readText())
                .setCancelable(false)
                .setNeutralButton("OK", DialogInterface.OnClickListener { dialog, _ ->
                    dialog.dismiss()
                })

            val alert = alertDialogBuilder.create()
            alert.setTitle("Error")
            alert.show()
        }
        else {
            alertDialogBuilder.setMessage("Response: ${response.body().toString()}")
                .setCancelable(false)
                .setNeutralButton("OK", DialogInterface.OnClickListener { dialog, _ ->
                    dialog.dismiss()
                })

            val alert = alertDialogBuilder.create()
            alert.setTitle("Error")
            alert.show()
        }
    }

    override fun onFailure(call: Call<String>, t: Throwable) {
        alertDialogBuilder.setMessage(t.message)
            .setCancelable(false)
            .setNeutralButton("OK", DialogInterface.OnClickListener {
                    dialog, _ -> dialog.dismiss()
            })

        val alert = alertDialogBuilder.create()
        alert.setTitle("Error")
        alert.show()
    }

})
.NET solution download from here and Android source code download from here here.

Creating never ending application v2

Details
Written by: Stanko Milosev
Category: Android
Published: 05 June 2022
Last Updated: 06 June 2022
Hits: 1123
  • kotlin
Here I gave one example of never ending application, which, unfortunately, after latest Huwaei update on my phone is not working. I gonna leave that article as is, and here I will show another possible example using mix of my original article and this and this answer.

I have added Service and BroadcastReceiver, in Manifest I have added permissions:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

Then in the MainActivity I have added the method "checkOptimization" and "openBatteryOptimization ":

@SuppressLint("NewApi", "BatteryLife")
private fun checkOptimization(context: Context) {
	val packageName = applicationContext.packageName
	val pm = applicationContext.getSystemService(POWER_SERVICE) as PowerManager
	if (!pm.isIgnoringBatteryOptimizations(packageName)) {
		val intent = Intent()
		intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
		intent.data = Uri.parse("package:" + context.packageName)
		context.startActivity(intent)
	}
}

fun openBatteryOptimization(context: Context) {
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
		val intent = Intent()
		intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
		context.startActivity(intent)
	} else {
		//Timber.d("Battery optimization not necessary")
	}
}
Both methods "checkOptimization" and "openBatteryOptimization" I have added to "onCreate" of MainActivity. For
Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
In import list I have added:
import android.provider.Settings
import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS

In Gradle I added:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
Service and receiver first I have copied from original article, then I have added following methods:
private val wakeLock: PowerManager.WakeLock by lazy {
	(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
		newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "neverEndingApplication:ServiceWakelock")
	}
}

private fun acquireWakelock() {
	try {
		wakeLock.let {
			wakeLock.setReferenceCounted(false)
			if (!wakeLock.isHeld) {
				wakeLock.acquire()
			}
		}
	} catch (e: RuntimeException) {
	}
}

private fun releaseWakelock() {
	try {
		wakeLock.let {
			if (it.isHeld) {
				it.release()
			}
		}
	} catch (e: RuntimeException) {
	}
}
Still in Service I have added acquireWakelock in onCreate:
override fun onCreate() {
	super.onCreate()
	acquireWakelock()
}
releaseWakelock() I have added when I want to stop the app:
if (intent?.action.equals("stopForeground")) {
	job?.cancel()
	releaseWakelock()
	stopForeground(true)
	stopSelfResult(startId)
}
MainActivity I also copied from my original article.

Example download from here.

  1. Disable / Enable manifest broadcast receiver
  2. Creating never ending application
  3. Send broadcast from separate service and update UI
  4. android:process=":MyService"

Page 3 of 11

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10