I have a class in my project housing a method to retrieve files (as a list). In order to write unit tests, and to be able to put everything into a versioning tool, I want put a small example directory into my project. However, when I do that, my method needs to be able to distinguish whether or not it should read from the real (project-external) path or the testing environment.
This is my method:
fun getDirectoryContentObject(baseUserDir: String): UserLicenses {
val dirExists = Files.exists(Paths.get(licenseLocation + baseUserDir))
if(!dirExists) {
return UserLicenses(baseUserDir, listOf())
}
val userLicenses = UserLicenses(baseUserDir, listOf())
Files.walk(Paths.get(licenseLocation + baseUserDir)).forEach { outerIt ->
val dirOrFileName = outerIt.fileName.toString()
if (dirOrFileName != baseUserDir && !dirOrFileName.endsWith(licenseFileExtension)) {
val fileList: MutableList<String> = mutableListOf()
Files.walk(Paths.get(outerIt.toString())).forEach { innerIt ->
val subDirOrFileName = innerIt.fileName.toString()
if (subDirOrFileName.endsWith(licenseFileExtension)) {
fileList += subDirOrFileName
}
}
userLicenses.licenseVersions += LicenseVersions(dirOrFileName, fileList)
}
}
return userLicenses
}
The licenseLocation value is set by #Value from application.yml and point to the files outside the project.
How can I tell the method to get the files from the inside the project if it is being executed by a unit test?
You can use spring resource api to get file instance.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/ResourceUtils.html
External Example:
filepath: file:/some-os-path/some-file-somewhere.abc
ResourceUtils.getFile(filepath);
Test Example:
filepath: classpath:file-from-resources-folder.abc
ResourceUtils.getFile(filepath);
Related
I am trying to expose a rest endpoint with camel. It will show a json data which is inside some .json files stored in s3 bucket. Also, it will filter by a date range.
First, I got some s3 objects informations in my Camel routes. (I am using kotlin)
//expose the endpoint
from("jetty:http://0.0.0.0:8080/getObjects")
.routeId("list-objects-on-bucket")
.to("aws-s3://[bucket-name]?amazonS3Client=#s3Client&operation=listObjects")
.process(ListObjects())
.to("direct:filter-list-from-s3")
then, I filter the data. (Till here everything is alright)
from("direct:filter-list-from-s3")
.routeId("filter-list-from-s3")
.process(FilterObjects())
.to("log:info")
But in my FilterObject class I do not know how to download every files that matches (look the if statement) and pass it to the next route that will treat them
class SaoMateusFilterObjects : Processor {
override fun process(exchange: Exchange?) {
val start_date = exchange!!.getIn().getHeader("start_date") as String
val end_date = exchange.getIn().getHeader("end_date") as String
val formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy")
val start = LocalDate.parse(start_date).format(formatter)
val end = LocalDate.parse(end_date).format(formatter)
val objectsNames = exchange!!.getIn().body as LinkedList<String>
for (objectName in objectsNames) {
if(objectName.contains(start) && objectName.contains(end) && objectName.contains(".json")) {
exchange.getIn() to "aws-s3://[bucket-name]?amazonS3Client=#s3Client&operation=getObject&fileName=$objectName"
}
}
}
}
Some problems are:
1 - I want to read. By I think I can't use the from() method. Because it can be use just once. So, the to() method is used to read.
2 - exchange.getIn().to("[s3-uri]") maybe/must be converted in S3Object(). How??
Can Someone help me with this?
Thank you
Instead of .to route, use .bean() and use the s3.getObject method to get the S3Object.
always Prefer using .bean() over .processor().
offical_s3_java_object operation sample.
We've been using a snippet like this one to rename the APK file generated by our Gradle build:
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "${variant.name}-${variant.versionName}.apk"
}
}
Source: https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration#variant_output
I am now in the process of converting my build.gradle to build.gradle.kts, i. e. to the Gradle Kotlin DSL. This is one of the last missing pieces: I can't figure out how to access outputFileName.
According to the API docs it does not even seem to exist:
BaseVariant.getOutputs() returns a DomainObjectCollection<BaseVariantOutput> which provides the all method used in the snippet.
BaseVariantOutput extends OutputFile which extends VariantOutput but none of these has an outputFileName or any getters or setters of a matching name.
So, I suspect there is some advanced Groovy magic at work to make this work - but how do I get there in Kotlin?
A little simplified version of #david.mihola answer:
android {
/**
* Notes Impl: Use DomainObjectCollection#all
*/
applicationVariants.all {
val variant = this
variant.outputs
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
.forEach { output ->
val outputFileName = "YourAppName - ${variant.baseName} - ${variant.versionName} ${variant.versionCode}.apk"
println("OutputFileName: $outputFileName")
output.outputFileName = outputFileName
}
}
}
Browsing through the source code of the Android Gradle plugin, I think I found the answer - here we go:
We are actually dealing with objects of type BaseVariantOutputImpl and this class does have both these methods:
public String getOutputFileName() {
return apkData.getOutputFileName();
}
public void setOutputFileName(String outputFileName) {
if (new File(outputFileName).isAbsolute()) {
throw new GradleException("Absolute path are not supported when setting " +
"an output file name");
}
apkData.setOutputFileName(outputFileName);
}
Using this knowledge we can now:
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
and then cast our target objects like so:
applicationVariants.all(object : Action<ApplicationVariant> {
override fun execute(variant: ApplicationVariant) {
println("variant: ${variant}")
variant.outputs.all(object : Action<BaseVariantOutput> {
override fun execute(output: BaseVariantOutput) {
val outputImpl = output as BaseVariantOutputImpl
val fileName = output.outputFileName
.replace("-release", "-release-v${defaultConfig.versionName}-vc${defaultConfig.versionCode}-$gitHash")
.replace("-debug", "-debug-v${defaultConfig.versionName}-vc${defaultConfig.versionCode}-$gitHash")
println("output file name: ${fileName}")
outputImpl.outputFileName = fileName
}
})
}
})
So, I guess: Yes, there is some Groovy magic at work, namely that Groovy's dynamic type system allows you to just access getOutputFileName and setOutputFileName (by way of the abbreviated outputImpl.outputFileName syntax, as in Kotlin) from your code, hoping they will be there at runtime, even if the compile time interfaces that you know about don't have them.
Shorter version using lambdas:
applicationVariants.all{
outputs.all {
if(name.contains("release"))
(this as BaseVariantOutputImpl).outputFileName = "../../apk/$name-$versionName.apk"
}
}
This will place APK into app/apk folder with name made of variant name and version code.
You can change the format of filename as you wish.
Important: it must be done only on release builds, because ".." in path corrupts debug build process with strange errors.
For libraryVariants it is possible to change output file name without accessing internal api:
libraryVariants.all {
outputs.all {
packageLibraryProvider {
archiveFileName.set("yourlibrary-${buildType.name}.aar")
}
}
}
For Kotlin KTS.
NOTE: This is considered a temporal solución, until a proper way to do it in KTS is released by Android team.
Working in AGP v7.1.2 it might work also in lower versions of AGP.
:app build.gradle
android {
// ...
this.buildOutputs.all {
val variantOutputImpl = this as com.android.build.gradle.internal.api.BaseVariantOutputImpl
val variantName: String = variantOutputImpl.name
val outputFileName = "custom-name-${variantName}.apk"
variantOutputImpl.outputFileName = outputFileName
}
}
I´m using Job DSL and I would like to download a file, read it, and set some env variables.
def static setSecrets(Job delegate, Map overrides = [:]) {
def liveUsername
def livePassword
def file
new URL('https://path/file').withInputStream { i ->
file.withOutputStream {
it << i
}
}
file.withReader { liveUsername = it.readLines().get(0) }
file.withReader { livePassword = it.readLines().get(1) }
def options = [
IDENTITY_USER: liveUsername,
IDENTITY_PASSWORD: livePassword]
setEnv(delegate, options, overrides)
}
This is the exception that I´m receiving
java.lang.NullPointerException: Cannot invoke method withOutputStream() on null object
Seems like the features of file cannot being used. But being in groovy file I was expecting can use the Job DSL templates and also, all the groovy features.
File is null so is throwing NPE when you call a method on it
def file
...
file.withOutputStream { // BANG!
I am trying to write a groovy script which will contain functions that are common to the SoapUI test Suite. Specifically I want to write a script that will contain all the logs that are output from the test suite.
GroovyScript1 will call a function in the GroovyScripts.groovy file. All is present in a SoapUI test suite.
I have not found any helpful advice on how to perform this task.
To specify again, I want to call a function contained in another Groovy Script.
Yes You can do this by following steps,
In your "GroovyScripts.groovy" file add below code,
class GLF
{
def log
def context
def testRunner
def GLF(logIn, contextIn, testRunnerIn)
{
this.log = logIn
this.context = contextIn
this.testRunner = testRunnerIn
}
//Till abobe line you must keep same code except class name
public String returnVal()
{
return 'Himanshu'
}
}
context.setProperty("Rt", new GLF(log, context, testRunner))
============================ END GroovyScripts.groovy ==========
Now in your "GroovyScript1" file you should use below code,
lib = testRunner.testCase.testSuite.project.testSuites["GroovyLibraryFunction"].testCases["TestCase 1"].testSteps["EndpointVerification"]
lib.run(testRunner, context)
def RT = context.Rt
def PT = RT.returnVal()
log.info PT
This way you can achive your target.
EDIT: Please let me be clear, I'm asking how to do this in Grails using Spring Dependency Injection, and NOT Grails' metaclass functionality or new().
I have a grails service that is for analyzing log files. Inside the service I use the current time for lots of things. For unit testing I have several example log files that I parse with this service. These have times in them obviously.
I want my service, DURING UNIT TESTING to think that the current time is no more than a few hours after the last logging statement in my example log files.
So, I'm willing to this:
class MyService {
def currentDate = { -> new Date() }
def doSomeStuff() {
// need to know when is "right now"
Date now = currentDate()
}
}
So, what I want to be able to do is have currentDate injected or set to be some other HARDCODED time, like
currentDate = { -> new Date(1308619647140) }
Is there not a way to do this with some mockWhatever method inside my unit test? This kind of stuff was super easy with Google Guice, but I have no idea how to do it in Spring.
It's pretty frustrating that when I Google "grails dependency injection" all I find are examples of
class SomeController {
// wow look how amazing this is, it's injected automatically!!
// isn't spring incredible OMG!
def myService
}
It feels like all that's showing me is that I don't have to type new ...()
Where do I tell it that when environment equals test, then do this:
currentDate = { -> new Date(1308619647140) }
Am I just stuck setting this property manually in my test??
I would prefer not to have to create a "timeService" because this seems silly considering I just want 1 tiny change.
Groovy is a dynamic language, and as such it allows you to do almost what you're asking for:
class MyServiceTests extends GrailsUnitTestCase {
def testDoSomeStuff() {
def service = new MyService()
service.currentDate = { -> new Date(1308619647140) }
// assert something on service.doSomeStuff()
}
}
Keep in mind this only modifies the service instance, not the class. If you need to modify the class you'll need to work with the metaClass. Take a look at this post by mrhaki.
Another option would be to make the current date a parameter to doSomeStuff(). That way you wouldn't need to modify your service instance.
Thanks for the help guys. The best solution I could come up with for using Spring DI in this case was to do the following in
resources.groovy
These are the two solutions I found:
1: If I want the timeNowService to be swapped for testing purposes everywhere:
import grails.util.GrailsUtil
// Place your Spring DSL code here
beans = {
if (GrailsUtil.environment == 'test') {
println ">>> test env"
timeNowService(TimeNowMockService)
} else {
println ">>> not test env"
timeNowService(TimeNowService)
}
}
2: I could do this if I only want this change to apply to this particular service:
import grails.util.GrailsUtil
// Place your Spring DSL code here
beans = {
if (GrailsUtil.environment == 'test') {
println ">>> test env"
time1(TimeNowMockService)
} else {
println ">>> not test env"
time1(TimeNowService)
}
myService(MyService) {
diTest = 'hello 2'
timeNowService = ref('time1')
}
}
In either case I would use the service by calling
timeNowService.now().
The one strange, and very frustrating thing to me was that I could not do this:
import grails.util.GrailsUtil
// Place your Spring DSL code here
beans = {
if (GrailsUtil.environment == 'test') {
println ">>> test env"
myService(MyService) {
timeNow = { -> new Date(1308486447140) }
}
} else {
println ">>> not test env"
myService(MyService) {
timeNow = { -> new Date() }
}
}
}
In fact, when I tried that I also had a dummy value in there, like dummy = 'hello 2' and then a default value of dummy = 'hello' in the myService class itself. And when I did this 3rd example with the dummy value set in there as well, it silently failed to set, apparently b/c timeNow blew something up in private.
I would be interested to know if anyone could explain why this fails.
Thanks for the help guys and sorry to be impatient...
Since Groovy is dynamic, you could just take away your currentDate() method from your service and replace it by one that suits your need. You can do this at runtime during the setup of your test.
Prior to having an instance of MyService instantiated, have the following code executed:
MyService.metaClass.currentDate << {-> new Date(1308619647140) }
This way, you can have a consistent behavior across all your tests.
However, if you prefer, you can override the instance method by a closure that does the same trick.
Let me know how it goes.
Vincent Giguère