Read local file in Android Studio JUnit test [duplicate] - java

In a unit test, how can I read data from a json file on my (desktop) file system, without hardcoding the path?
I would like to read test input (for my parsing methods) from a file instead of creating static Strings.
The file is in the same location as my unit testing code, but I can also place it somewhere else in the project if needed. I am using Android Studio.

Depending on android-gradle-plugin version:
1. version 1.5 and higher:
Just put json file to src/test/resources/test.json and reference it as
classLoader.getResource("test.json").
No gradle modification is needed.
2. version below 1.5: (or if for some reason above solution doesn't work)
Ensure you're using at least Android Gradle Plugin version 1.1. Follow the link to set up Android Studio correctly.
Create test directory. Put unit test classes in java directory and put your resources file in res directory. Android Studio should mark them like follow:
Create gradle task to copy resources into classes directory to make them visible for classloader:
android{
...
}
task copyResDirectoryToClasses(type: Copy){
from "${projectDir}/src/test/res"
into "${buildDir}/intermediates/classes/test/debug/res"
}
assembleDebug.dependsOn(copyResDirectoryToClasses)
Now you can use this method to get File reference for the file resource:
private static File getFileFromPath(Object obj, String fileName) {
ClassLoader classLoader = obj.getClass().getClassLoader();
URL resource = classLoader.getResource(fileName);
return new File(resource.getPath());
}
#Test
public void fileObjectShouldNotBeNull() throws Exception {
File file = getFileFromPath(this, "res/test.json");
assertThat(file, notNullValue());
}
Run unit test by Ctrl+Shift+F10 on whole class or specyfic test method.

For local unit tests (vs. instrumentation tests), you can put files under src/test/resources and read them using classLoader. For example, following code opens myFile.txt file in the resources directory.
InputStream in = this.getClass().getClassLoader().getResourceAsStream("myFile.txt");
It worked with
Android Studio 1.5.1
gradle plugin 1.3.1

In my case, the solution was to add to the gradle file
sourceSets {
test.resources.srcDirs += 'src/unitTests/resources'
}
After it everything was found by AS 2.3.1
javaClass.classLoader.getResourceAsStream("countries.txt")

I though I should add my findings here. I know this is a little old but for the newer versions of Gradle, where there is NO src/test/resources directory, but only one single resources directory for the whole project, you have to add this line to your Gradle file.
android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
By doing this you can access your resource with:
this.getClass().getClassLoader().getResourceAsStream(fileName);
I've been searching for this and could not find an answer, so I decided to help others here.

I've had plenty of problems with test resources in Android Studio so I set up a few tests for clarity. In my
mobile (Android Application) project I added the following files:
mobile/src/test/java/test/ResourceTest.java
mobile/src/test/resources/test.txt
mobile/src/test/resources/test/samePackage.txt
The test class (all tests passes):
assertTrue(getClass().getResource("test.txt") == null);
assertTrue(getClass().getResource("/test.txt").getPath().endsWith("test.txt"));
assertTrue(getClass().getResource("samePackage.txt").getPath().endsWith("test/samePackage.txt"));
assertTrue(getClass().getResource("/test/samePackage.txt").getPath().endsWith("test/samePackage.txt"));
assertTrue(getClass().getClassLoader().getResource("test.txt").getPath().endsWith("test.txt"));
assertTrue(getClass().getClassLoader().getResource("test/samePackage.txt").getPath().endsWith("test/samePackage.txt"));
In the same root project I have a Java (not Android) project called data. If I add the same files to the data project:
data/src/test/java/test/ResourceTest.java
data/src/test/resources/test.txt
data/src/test/resources/test/samePackage.txt
Then all the tests above will fail if I execute them from Android Studio, but they pass on the command line with ./gradlew data:test.
To get around it I use this hack (in Groovy)
def resource(String path) {
getClass().getResource(path) ?:
// Hack to load test resources when executing tests from Android Studio
new File(getClass().getClassLoader().getResource('.').path
.replace('/build/classes/test/', "/build/resources/test$path"))
}
Usage: resource('/test.txt')
Android Studio 2.3, Gradle 3.3

If you go to Run -> Edit configurations -> JUnit and then select the run configuration for your unit tests, there is a 'Working directory' setting. That should point to wherever your json file is. Keep in mind this might break other tests.

Actually, this is what worked for me with Instrumentation Tests (Running Android Studio version 3.5.3, Android Gradle Plugin version 3.5.3, Gradle version 6.0.1):
Put your files in src/androidTest/assets folder
In your test file:
InputStream is = InstrumentationRegistry.getInstrumentation().getContext().getAssets().open("filename.txt");

In my case I only needed to create resources folder into test folder and put my resource file in that folder.
Then, in the test simply load the file as resource stream with:
val inputStream =
this.javaClass.classLoader?.getResourceAsStream("gallery.xml")
Reference on medium: https://medium.com/mobile-app-development-publication/android-reading-a-text-file-during-test-2815671e8b3b

Step(1) open Android Studio select Project view
Step(2) and create resources directory under test.
Please look at attached screenshot for Step(2) result
Step(3) put json file into resources folder(app/src/test/resources)
Please look at attached screenshot for Step(3) result
Step(4) Create a common class to handle reading local json files and converting to respective models using Gson
Example :-
object TestHelper {
private val gson = Gson()
fun loadJsonAsString(fileName: String): String {
val inputStream = javaClass.getResourceAsStream("/$fileName")
return getStringFromInputStream(inputStream)
}
#Throws(IOException::class)
private fun getStringFromInputStream(stream: InputStream?): String {
var n = 0
val buffer = CharArray(1024 * 4)
val reader = InputStreamReader(stream, "UTF8")
val writer = StringWriter()
while (-1 != reader.read(buffer).also { n = it }) writer.write(buffer, 0, n)
return writer.toString()
}
fun <T> convertJsonToModel(jsonString: String, classT: Class<T>): T{
return gson.fromJson(jsonString, classT)
}
}
Step(5) load the json file stored locally in resources directory created in Step(2)
val GET_USER_INFORMATION_RESPONSE_FILE_NAME = "user_response.json"
val jsonString = loadJsonAsString(GET_USER_INFORMATION_RESPONSE_FILE_NAME)
val networkStatusResponse =
convertJsonToModel(jsonString, UserResponse::class.java)
Step(6) at the end of Step(5) you would have converted local json file into required model class that can be used to write your unit tests.

Related

JDK Mission Control: Modifying Stack data from jfr files

Like this question - I'm trying to load in an existing jfr file that has been recorded on another machine external to our organisation. I now want to deobfuscate the information, either as a plugin for JDK Mission Control, or as a utility for reading in a jfc file and writing out a de-obfuscated version.
My class does the relevant implementation of the API
public class JFRProcessor implements IParserExtension {
//impementation details below
And I have tested it (successfully) with the following
List<File> files = new ArrayList<>();
files.add(new File("/user/rafe/Input001.jfr"));
List<IParserExtension> extensions = new ArrayList<>();
extensions.add(new JFRProcessor());
IItemCollection events = JfrLoaderToolkit.loadEvents(files, extensions);
//write out to xml to validate the change
RecordingPrinter printer = new RecordingPrinter(new PrintWriter(new File("/user/rafe/Output0001.xml")), Verbosity.HIGH, false);
printer.print(events);
When I then try to export this as a jar, I have the fully qualified classname (com.extension.JFRProcessor) in the relevant META-INF/services/org.openjdk.jmc.flightrecorder.parser.IParserExtension file - and JDK Mission Control doesn't do anything with the plugin (when put in the drop-ins directory).
This was then verified by exporting the jar and in a separate project (with the exported jar in the build path):
ServiceLoader<IParserExtension> loader = ServiceLoader.load(IParserExtension.class,
IParserExtension.class.getClassLoader());
Another approach that I took was to write out the events:
I have also tried using the latest SNAPSHOT release of JDK Mission Control with the new Recordings class in org.openjdk.jmc.flightrecorder.writer.api but I am struggling to see how to get between the IItemCollection and any useful data to feed into the Recording instance that I'm trying to rewrite into.
final Recording rec = Recordings.newRecording("/user/rafe/Output-001.jfr");
events.forEach(event -> {
IType<IItem> type = event.getType();
rec.writeEvent(typedValue);
});
Any help would be appreciated for either approach - as I'm struggling to see how to use this without de-obfuscating the data first!

CDK ECR: How to set repository name and tag

How to set repository name and tag on DockerImageAsset of CDK?
This is my code: (groovy)
private uploadImage(String name, File directory, File jarFile) {
def asset = DockerImageAsset.Builder.create(scope, cdkId("$name-image"))
.directory(directory.toString())
.buildArgs([
"jarFile" : jarFile.name,
"environment": config.environment
])
.build()
println "ImageURL: $asset.imageUri"
}
This is the image url printed:
ImageURL: 9999999999999.dkr.ecr.us-west-2.${Token[AWS.URLSuffix.1]}/aws-cdk/assets:89ae89e0b3f7652a4ac70a4e18d6b2acec2abff96920e81e6487da58c8b820f3
I guess this is meant to be this way for CI/CD, where it doesn't matter the repository name/tag.
But if you need to use this image outside of CI/CD environment, it turns into a mess as we have many different projects and versions in the same repository (aws-cdk/assets).
I see a method called "repositoryName" but it is deprecated and I couldn't find an alternative or an explanation of why it is deprecated.
This isnt a direct answer to your question, as this solution uses NodeJS rather than Groovy, but it may help others that end up here.
You could check out the cdk-ecr-deployment library in the cdklabs repository, which gives a construct allowing you to deploy docker images to ECR.
This is done in three steps:
Create the repo
const repo = new ecr.Repository(this, 'NginxRepo', {
repositoryName: 'nginx',
removalPolicy: RemovalPolicy.DESTROY,
});
Create the image
const image = new DockerImageAsset(this, 'CDKDockerImage', {
directory: path.join(__dirname, 'docker'),
});
Tag and deploy the image to the repo
new ecrDeploy.ECRDeployment(this, 'DeployDockerImage', {
src: new ecrDeploy.DockerImageName(image.imageUri),
dest: new ecrDeploy.DockerImageName(`${repo.repositoryUri}:latest`),
});
See the example for more context on how this is used.

Gradle build dependency form external go project

I wanted to give the dependency location from the internal file system instead of the git location.
Bellow is the part of the build.gradle file. I have some internal changes to the incubator-openwhisk-client-go/whisk project in the local mechine, so I wanted to add this local depenancy instead of the github original location. How Do I achive this?
plugins {
id 'com.github.blindpirate.gogradle' version '0.8.1'
}
golang {
packagePath = 'github.com/apache/incubator-openwhisk-cli' as String
buildTags = (rootProject.findProperty('goTags')?:'').split(',')
}
dependencies {
golang {
// BEGIN - Imported from Godeps
build(['name':'golang.org/x/sys/unix', 'version':'7f918dd405547ecb864d14a8ecbbfe205b5f930f', 'transitive':false])
build(['name':'gopkg.in/yaml.v2', 'version':'cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b', 'transitive':false])
build(['name':'github.com/ghodss/yaml', 'version':'0ca9ea5df5451ffdf184b4428c902747c2c11cd7', 'transitive':false])
build(['name':'github.com/apache/incubator-openwhisk-client-go/whisk','version':'e452b524cd745f71c913c5acccf72a8daba6dc71','transitive':false])
// END - Imported from Godeps
test name:'github.com/stretchr/testify', version:'b91bfb9ebec76498946beb6af7c0230c7cc7ba6c', transitive:false //, tag: 'v1.2.0'
test name:'github.com/spf13/viper', version:'aafc9e6bc7b7bb53ddaa75a5ef49a17d6e654be5', transitive:false
test name:'github.com/cpuguy83/go-md2man/md2man', version:'1d903dcb749992f3741d744c0f8376b4bd7eb3e1', transitive:false //, tag:'v1.0.7'
test name:'github.com/davecgh/go-spew/spew', version:'346938d642f2ec3594ed81d874461961cd0faa76', transitive:false //, tag:'v1.1.0'
test name:'github.com/pmezard/go-difflib/difflib', version:'792786c7400a136282c1664665ae0a8db921c6c2', transitive:false
}
}
full gradle file is available https://github.com/apache/incubator-openwhisk-cli/blob/master/build.gradle

How to access version of my project in build.gradle from a Java class

I'm quite new to Gradle so the answer might be simple, so I apologize if the answer is simple: I have a testing tool that needs to fetch it's version and compare it to the version of the application it is testing. However , the version of my tool is in my build.graddle as
version '1.0'
I tried different way to access it ( such as) :
task generateSources {
File outDir
outDir = file("$buildDir/classes/test/tests/Version.java")
doFirst {
outDir.exists() || outDir.mkdirs()
new File(outDir).write("public class Version { public static final String VERSION = \"$project.version\"; }")
}
}
compileJava.dependsOn generateSources
compileJava.source generateSources.outputs.files, sourceSets.main.java
I found this piece of code to output the version to another java file, but I fail to see how I'd be able to retrieve that info afterwards ( I mean, my tests are defined in src and I would need to point to a file that doesn't exist at compilation -- correct me if I'm wrong here).
Any idea on how I could accomplish this task?
First of all, you are trying to create java source file in your build/classes (it should contain compiled classes, not sources) directory, but you have to do it in your sources, otherwise it won't be compiled. And if you need this new class to be vailable not for tests, then use src/main/java, not src/test/java/
But anyway, I suppose for your case it's much easier to use some properties file for that and replace some token within it during build. That will allow you to make some static logic to get this property value and use it yet before running the build. So all you need is:
1- to have some properties file in your resources src/main/resources (for example app.properties), where should version variable be stored, with it's value like APP_VERSION_TOKEN
version=%APP_VERSION_TOKEN%
2- configure you Gradle processResources to replace tokens, something like this:
processResources {
filesMatching('**/app.properties') {
filter {
it.replace('%APP_VERSION_TOKEN%', version)
}
}
}
3- make some method to read this file and return the value of the property and use it where you need.
And that's all. For unit tests you can have another file with the same name under src/test/resource with the unchanging value you need for testing.

Gradle : Exclude certain files from packaging (jar) using annotations

I am looking for a solution to exclude certain files marked with a particular annotation to be packaged in jar (can be compiled but not part of jar created).
I have tried the following steps
Create a ClassLoader using : sourceSets.main.output + configurations.runtime
Check recursively within the compiled classes, use ClassLoader.loadClass to load the class and check if annotation is present (Class.isAnnotationPresent)
Any pointers would be helpful.
I was able to implement this long time back but forgot I had posted the question here.
The solution I used to was actually quite simple -
Using gradle jar task -
jar {
excludes = excludedFiles(sourceSets.main.allSource.files)
baseName = artifactName
version = artifactVersion
}
And define the excludedFiles function to look up the files in the source directory provided -
def excludedFiles(Collection<File> files) {
List<String> classes = new ArrayList<>()
files.each { file ->
if (file.isDirectory()) {
excludedFiles(Arrays.asList(file.listFiles()))
}
else {
if (file.text.contains("#YourAnnotation") && file.text.contains("import foo.bar.YourAnnotation")) {
classes += getClassName(file.absolutePath)
}
}
}
return classes
}
Hope this helps.

Categories

Resources