I'm trying to cover as much as possible a Kotlin Android library and I'm encountering an issue about custom BuildConfig variable, better known as buildConfigField.
I would like to mock this variable to test both true and false values.
Extract from Gradle file :
android {
defaultConfig {
buildConfigField "boolean", "ENABLE_LOG", "false"
}
flavorDimensions "log"
productFlavors {
loggable {
buildConfigField "boolean", "ENABLE_LOG", "true"
dimension "log"
}
notloggable {
dimension "log"
}
}
}
Extract of the Kotlin function to be tested :
fun buildClient(): MyClient {
var myClientBuilder : MyClient.Builder = MyClient.Builder();
if (BuildConfig.ENABLE_LOG) {
val interceptor = LoggingInterceptor();
interceptor.setLevel(LoggingInterceptor.Level.ALL);
myClientBuilder.addInterceptor(interceptor);
}
return myClientBuilder.build()
}
Unit test :
#Test
fun buildClient_enableLog_oneInterceptor() {
// GIVEN
Mockito.mock(BuildConfig::class.java)
Mockito.doReturn(true).`when`(BuildConfig.ENABLE_LOG)
// WHEN
val myClient = myService!!.buildClient()
// THEN
assertNotNull(myClient)
assertNotNull(myClient.interceptors())
assertEquals(1, myClient.interceptors().size)
}
I tried different things and it never works.
If someone have already done this work, it can help me a lot (and others I guess).
Thanks
ReflectionHelpers.setStaticField(BuildConfig::class.java, "ENABLE_LOG", true)
By default, all tests run against the debug build type. You can change this to another build type by using the testBuildType property in your module-level build.gradle file. For example, if you want to run your tests against your "staging" build type, edit the file as shown in the following snippet.
android {
...
testBuildType "staging"
}
but this is causing other options to fail
Little late to the party but this is how you should test any thing related to BuildConfig file.
BuildConfig.java is generated for each variant of your app. In your case you have atleast 4 variants.
LoggableDebug
LoggableRelease
NotloggableDebug
NotloggableRelease
ENABLE_LOG will be false for options 3 and 4.
If you want to Unit test this, I recommend writing UnitTest in src/testNotLoggable/java/com/.../TestFile.java.
In that TestFile.java your BuildConfig.ENABLE_LOG should be false.
You can check BuildConfig.java file for each variant under /build/source/buildConfig/flavorname/debug/com/project/../BuildConfig.java
Related
We have an App in Kotlin ( Android Stdio) which has different constants by environment.
We are using Constants.kt
const val IMAGES_API = "https://localhost:3000/v1/images"
and we want to use the same variable in staging/qa/prod.
The App is building in Kotlin and we are using gradle (groovy scripts) to compiling and packing the different environment staging/qa/prod.
My first approach has been to create this properties on the gradle.properties and load the properties on the build.gradle file like this :
def loadProperties() {
def props = new Properties()
file("gradle.properties").withInputStream { props.load(it) }
def config = props
project.ext.config = config
}
And when I run gradle I can see the new properties, but I don't know how to get this value inside the App ( in the kotlin code).
My only idea is to create a task on build.gradle to copy a Constants.kt file by environment. But, I don't think, it's a good practice. I think, there must be another way to set different variables in the App.
Please, can anybody help me with this?
What you want is to configure build types in your app module's gradle file with buildConfigField in each:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField "String", "SERVER_URL", '"http://prod.this-is-so-fake.com"'
}
debug {
applicationIdSuffix ".debug"
debuggable true
buildConfigField "String", "SERVER_URL", '"http://test.this-is-so-fake.com"'
}
/**
* The `initWith` property allows you to copy configurations from other build types,
* then configure only the settings you want to change. This one copies the debug build
* type, and then changes the application ID.
*/
staging {
initWith debug
applicationIdSuffix ".debugStaging"
buildConfigField "String", "SERVER_URL", '"http://prod.this-is-so-fake.com"'
}
}
In code, you can refer to BuildConfig.SERVER_URL, and it will be populated with the string based on the build type you choose at compile time.
You can build different apk/app bundles to distribute.
Referencing this answer .
EDIT As an aside, in real life I have found this approach to be... annoying. It is easier to bundle inside the app a toggle that allows QA to switch between environments. This way you only have one bundle to deal with.
I am generating multiple apk's based on different config files.
So i defined a variable on build.gradle file
def isPRODBuild = false
I want to change this variable in productFlavors scope, so i did following.
productFlavors {
dev {
isPRODBuild= false
}
prod {
isPRODBuild= true
}
}
But it seems like that its not changing the value of isPRODBuild value.
Do in need to use some other type of variable in build.gradle file?
At build time, Gradle generates the BuildConfig class so your app code
can inspect information about the current build. You can also add
custom fields to the BuildConfig class from your Gradle build
configuration file using the buildConfigField() method and access
those values in your app's runtime code. Likewise, you can add app
resource values with resValue().
So you could simply use buildConfigField to define your variable.
productFlavors {
dev {
buildConfigField "boolean", "isPRODBuild", "false"
}
prod {
buildConfigField "boolean", "isPRODBuild", "true"
}
}
Then to access the aforementioned variable simply use BuildConfig.isPRODBuild
I have 2 build types of my application: debug and release.
I want to execute tests on both build types.
But currently only one Build Type is tested. By default it is the debug Build Type, but this can be reconfigured with:
android {
...
testBuildType "release"
}
I want to execute connectedDebugAndroidTest and connectedReleaseAndroidTest both one by one without changing gradle file.
Is it possible to make "testBuildType" conditional ?
So that according to build variant in gradle task (connectedDebugAndroidTest and connectedReleaseAndroidTest), it will execute tests on that build.
I am not sure but this is worked for me. If you want to execute code according to building variable (debug and released) in app then you can do by using following code.
This is for Activity java file.
public void printMessage()
{
if (BuildConfig.DEBUG)
{
//App is in debug mode
}
else
{
//App is released
}
}
If you want to check in build.gradle file then do by following code.
First way
buildTypes {
debug {
buildConfigField "String", "SERVER_URL", '"http://test.this-is-so-fake.com"'
}
release {
buildConfigField "String", "SERVER_URL", '"http://prod.this-is-so-fake.com"'
}
mezzanine.initWith(buildTypes.release)
mezzanine {
buildConfigField "String", "SERVER_URL", '"http://stage.this-is-so-fake.com"'
}
}
Second way
android {
testBuildType obtainTestBuildType()
}
def obtainTestBuildType() {
def result = "debug";
if (project.hasProperty("testBuildType")) {
result = project.getProperties().get("testBuildType")
}
result
}
For detail please check this, this and this stackoverflow answer.
I hope you will get your solution.
I have search Gradle official website and Android Developer official website, but could not find an answer to this.
In android build.gradle, what is the "it." in the below buildConfigField method? Is this an instance of an object?
I found that moving the buildConfigField method up to defaultConfig lets me use the method without the "it."
What is the difference between the two? Why might I use one method over the other?
android {
...
defaultConfig {
...
buildConfigField 'String', 'API_KEY', MyApiKey
}
buildTypes {
release {
...
}
}
buildTypes.all {
...
}
buildTypes.each {
...
it.buildConfigField 'String', 'API_KEY', MyApiKey
}
}
Gradle build scripts are enhanced Groovy scripts. When you see "it" in a Groovy script or a Gradle build script, it represents the object that was passed into a closure. In your example, the closure is the "{...}" that was passed to "each". So, it iterates through the "buildTypes" collection (or something that is iterable), passes each entry to the closure, and you referenced the passed object as "it". You can change the name of the object passed to the closure, but it's "it" by default.
I'm developing in eclipse using ADT for android.
In my application I have some constants which help me to to debug my app easily.
As an example I have:
public static final boolean DEBUG_TOAST_LOGS = true;
which help me to toast some logs on the screen.
Each time I intend to build a release, I have to go through my
constants and set their values to what is appropriate for the release edition, which is somehow painful.
Now what I want is a way to build my app, using two configurations: one for debug mode and the other for release mode. The release mode should set my constants to the appropriate values. How cant I do that? What's your suggestion? What's the best way to accomplish my need?
Any help would be appreciated.
I'm not sure if you are using Gradle as your build system. If you do, you can set build-type specific resources, e.g. a boolean debug value will be true for debug build type, and false for release build type.
build.gradle
android {
defaultConfig {
...
resValue "bool", "debug", "true"
}
buildTypes {
release {
...
resValue "bool", "debug", "false"
}
}
...
}
Application.java
public class Application extends android.app.Application {
#Override
public void onCreate() {
super.onCreate();
if (getResources().getBoolean(R.bool.debug)) {
... // debug logic here
}
...
}
}
#hidro's solution is fine, but requires an unnecessary getResources()... call each time you want to access the value.
There's another possibility :
build.gradle
android {
buildTypes {
debug {
buildConfigField "boolean", "DEBUG_TOAST_LOGS", "true"
}
release {
buildConfigField "boolean", "DEBUG_TOAST_LOGS", "false"
}
}
}
Then,in your code, you can write :
if (BuildConfig.DEBUG_TOAST_LOGS) {
// ... enjoy your toasts ...
}