Verifying Jenkins calls while testing pipeline code - jenkins

I am writing a Jenkins pipeline library, and am having some difficulties with mocking/validating an existing Jenkins pipeline step.
I am using jenkins-spock by homeaway to unit test, but I think my problem is more Spock related.
import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification
import com.company.pipeline.providers.BuildLogProvider
class PublishBuildLogSpec extends JenkinsPipelineSpecification {
BuildLogProvider buildLogProvider = Mock()
PublishBuildLog publishBuildLog
def setup () {
publishBuildLog = new PublishBuildLog(buildLogProvider: buildLogProvider)
explicitlyMockPipelineStep('writeFile')
}
def "Gets the log file contents for a specific job and build"() {
when:
"the call method is executed with the jobName and buildNumber parameters set"
publishBuildLog.call("JOBNAME", "42")
then:
"the getBuildLog on the buildLogProvider is called with those parameters"
1 * buildLogProvider.getBuildLog("JOBNAME", "42")
}
def "the contents of log file is written to the workspace"() {
given:
"getBuildLog returns specific contents"
def logFileText = "Example Log File Text"
buildLogProvider.getBuildLog(_, _) >> logFileText
when:
"publishBuildLog.call is executed"
publishBuildLog.call(_, _)
then:
"the specific contents is passed to the writeFile step"
1 * getPipelineMock("writeFile").call([file: _ , text: logFileText])
}
}
This is my unit test. I am attempting to say that writeFile is called with the text matching the contents of logFileText, ignoring what the other parameters are. I have tried numerous combinations, but always seem to get the same or similar response to response of:
Too few invocations for:
1 * getPipelineMock("writeFile").call([file: _ , text: "Example Log File Text"]) (0 invocations)
Unmatched invocations (ordered by similarity):
1 * (explicit) getPipelineMock("writeFile").call(['file':'filename', 'text':'Example Log File Text'])
This is to test this class
import com.company.pipeline.providers.BuildLogProvider
class PublishBuildLog {
BuildLogProvider buildLogProvider = new BuildLogProvider()
void setBuildLogProvider(BuildLogProvider buildLogProvider) {
this.buildLogProvider = buildLogProvider
}
def call(def jobName, def buildNumber) {
def contents = buildLogProvider.getBuildLog(jobName, buildNumber)
writeFile(file: "filename", text: contents)
}
}
I am at a loss as to how to validate this call. I have a lot of experience with Java and Junit, but I am relatively new to Spock.
How can I verify this?

For me your test passes. But there is one thing I find strange: You use jokers in a when: block where you should really use concrete parameters like in the first feature method:
when: "publishBuildLog.call is executed"
publishBuildLog.call(_, _)
Instead you should write:
when: "publishBuildLog.call is executed"
publishBuildLog.call("JOBNAME", "42")
For me this works just fine if I use this as a dummy class in order to make the code compile (because you did not provide the source code):
class BuildLogProvider {
def getBuildLog(def jobName, def buildNumber) {}
}

Related

Jenkinsfile syntax error: No such property

I'm trying to test a binary with latest commit id in Jenkins. Error happens at send slack message stage:
def kResPath = "/tmp/res.json" // global variable, where json file is dumped to; declared at the very beginning of Jenkinsfile
def check_result_and_notify(tier, result) {
def kExpected = 3
def kSuccessSlackColor = "#00CC00"
message = "Test ${tier} result: ${result}\n"
def test_result = readJSON file: kResPath
// 0 means index, "benchmarks", "real-time" is the key
real_time = test_result["benchmarks"][0]["real_time"]
if (real_time > kExpected) {
message += String.format("real time = %f, expected time = %f", real_time, kExpected)
}
slackSend(color: ${kSuccessSlackColor}, message: message.trim(), channel: "test-result")
}
The json file looks like:
{
"benchmarks": [
{
"real_time": 4,
},
{
"real_time": 5,
}
],
}
The error message I've received is hudson.remoting.ProxyException: groovy.lang.MissingPropertyException: No such property: kResPath for class: WorkflowScript
Can someone tell me what's wrong with my code?
Is there any way I could test it locally so that I don't need to commit it every single time? I googled and find it needs server id and password, which I don't think accessible to me :(
Your kResPath variable is undefined in the scope of that function or method (unsure which based on context). You can pass it as an argument:
def check_result_and_notify(tier, result, kResPath) {
...
}
check_result_and_notify(myTier, myResult, kResPath)
and even specify a default if you want:
def check_result_and_notify(tier, result, kResPath = '/tmp/res.json')

get String value from Jenkins console output

In the Jenkins output, I had the following assert error
but I need to get the String error from the error assert or any text. I'm using I'm My JenkinsFile:
def matcher = manager.getLogMatcher('.*Delete organization Account failed: *')
but generates the following error:
So I just want to check, that the log contains a specific string and if the texts exists make the build failed currentBuild.result = "FAILED", saving the text to send it by slack
You can put the condition in below way :
if (manager.logContains('.*Delete organization Account failed:*')) {
error("Build failed because of Delete organization Account..")
}
This is how it worked for me:
import hudson.model.*
node {
.....
if (Slack.toBoolean() ) {
def matcher = manager.getLogMatcher(".*Error.*")
if(matcher.matches()) {
pbn=matcher.group(0)
println pbn
slack_message = "`BUILD ERROR`: ${pbn} "
println slack_message
matcher = null // fix NotSerializableException
slackSend(channel: "#reports", message: slack_message, color: '#172530');
}
}
}

function lines after httpRequest are not executed in groovy jenkins pipeline

None of the lines after making httpRequest are getting executed. Everything else works fine in this function. What could be going wrong here?
However, network request is going fine and I am able to see the response in the console. httpRequest is being made via plugin
I've even tried CURL - but lines after curl are not executed.
#NonCPS
def doPRCommentBasedTesting() {
def causes = currentBuild.rawBuild.getCauses()
def commentURL
for(cause in causes) {
if (cause.class.toString().contains("GitHubPullRequestCommentCause")) {
commentURL = cause.getCommentUrl()
commentURL = commentURL.substring(commentURL.lastIndexOf("-") + 1)
println "This job was caused by job " + commentURL
def url1 = "https://<git_url>/api/v3/repos/<owner>/<repo>/issues/comments/" + commentURL
def commentText = httpRequest authentication: '<auth_cred>', url: url1, consoleLogResponseBody: true
println commentText
println commentText.getClass()
println "hello world, how are you doing today?"
}
else {
println "Root cause : " + cause.toString()
}
}
println "==============================="
return 0
}
A non cps function does not have the ability to pause in between because it runs in a go. You need to put network call into a different function that is not marked as nonCPS and then it will work. In general the nonCPS block should be very small and limited to code that cannot be serialised

Jenkins Pipeline Execute Multiple FreeStyleProjects in Parallel [duplicate]

The script is not iterating through all the values of the 'modules' array.
class Module {
public String name = '';
public Boolean isCustom = false;
public Module(String name, Boolean custom){
this.name = name;
this.isCustom = custom;
}
}
//creates array from the ext_module env var
modules = [];
EXT_MODULE.split(',').each {
modules.add(new Module(it, false));
}
println modules;
modules.each {
println "MODULE NAME ::::: ${it.name}"
if(it.isCustom)
{
println "install custom";
} else {
println "install non custom";
}
};
This is the result of the run. The array shows 4 elements, but the code inside the .each black only executes once.
Running: Print Message
[Module#71f09325, Module#e1ddb41, Module#7069a674, Module#1f68f952]
Running: Print Message
MODULE NAME ::::: puppetlabs-ntp
Running: Print Message
install non custom
Running: End of Workflow
Finished: SUCCESS
The messages "Running: Print Message" and "Running: End of Workflow" indicate that you are using the new workflow plugin: https://wiki.jenkins-ci.org/display/JENKINS/Workflow+Plugin. This plugin currently has a bug causing at least some Groovy iterations involving a closure to be aborted after one iteration: https://issues.jenkins-ci.org/browse/JENKINS-26481
The workaround is to simply use an old school for loop (code below).
Also, NonCPS is another workaround.
There is an open issue for this matter. See here: https://issues.jenkins-ci.org/browse/JENKINS-26481
Update, Oct 24th, 2016
/**
* Dumps environment varibles to the log, using an old school for loop.
*/
import com.cloudbees.groovy.cps.NonCPS
def version = '1.0'
#NonCPS
def dumpEnvVars() {
def str = "Dumping build environment variables...\n"
for (Map.Entry<String, String> entry : currentBuild.build().environment) {
str += " ${entry.key} = ${entry.value}\n"
}
echo str
}
return this;
As of yesterday, the new Pipeline plugin was delivered in version 2.0 and correct this problem.
.each closures now work, but .collect still only iterate once.

How to partially mock service in Grails integration test

I am attempting to write a test for a controller which calls a service method. I would like to mock a dependent method within that service.
My spec is as follows:
MyController myController = new MyController()
def mockMyService
def "My spy should be called"() {
when:
mockMyService = Spy(MyService) {
methodToSpy() >> {
println "methodToSpy called"
} // stub out content of this fn
}
myController.myService = mockMyService
myController.callService()
then:
1 * mockMyService.methodToSpy()
}
When I attempt to run this test, I get the following error:
Failure: |
My spy should be called(spybug.MyControllerSpec)
|
Too few invocations for:
1 * mockMyService.methodToSpy() (0 invocations)
Unmatched invocations (ordered by similarity):
1 * mockMyService.serviceMethod()
1 * mockMyService.invokeMethod('methodToSpy', [])
1 * mockMyService.invokeMethod('println', ['in serviceMethod about to call methodToSpy'])
1 * mockMyService.invokeMethod('println', ['Back from methodToSpy'])
As you can see, Spock is capturing the Groovy invokeMethod call, not the subsequent call to the actual method. Why is this happening?
The complete project is available here.
Try this:
def "My spy should be called"() {
given:
mockMyService = Mock(MyService)
myController.myService = mockMyService
when:
myController.callService()
then:
1 * mockMyService.methodToSpy(_) >> { println "methodToSpy called" }
}
According to the spock documentation for stubs, if you want to use the cardinality, you must use a Mock and not a Stub.
http://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html#_stubbing

Resources