How to not mark Jenkins job as FAILURE when pytest tests fail - jenkins

I have a Jenkins setup with a pipeline that uses pytest to run some test suites. Sometimes a test fails and sometimes the test environment crashes (random HTTP timeout, external lib error, etc.). The job parses the XML test result but the build is marked as FAILURE as long as pytest returns non-zero.
I want Jenkins to get exit code zero from pytest even if there are failed tests but I also want other errors to be marked as failures. Are there any pytest options that can fix this? I found pytest-custom_exit_code but it can only suppress the empty test suite error. Maybe some Jenkins option or bash snippet?
A simplified version of my groovy pipeline:
pipeline {
stages {
stage ('Building application') {
steps {
sh "./build.sh"
}
}
stage ('Testing application') {
steps {
print('Running pytest')
sh "cd tests && python -m pytest"
}
post {
always {
archiveArtifacts artifacts: 'tests/output/'
junit 'tests/output/report.xml'
}
}
}
}
}
I have tried to catch exit code 1 (meaning some tests failed) but Jenkins still received exit code 1 and marks the build as FAILURE:
sh "cd tests && (python -m pytest; rc=\$?; if [ \$rc -eq 1 ]; then exit 0; else exit \$rc; fi)"

Your attempt does not work because Jenkins runs the shell with the errexit (-e) option enabled and that causes the shell to exit right after the pytest command before it reaches your if statement. There is a one-liner however that will work because it is executed as one statement: https://stackoverflow.com/a/31114992/1070890
So your build step would look like this:
sh 'cd tests && python -m pytest || [[ $? -eq 1 ]]'

My solution was to implement the support myself in pytest-custom-exit-code and create a pull request.
From version 0.3.0 of the plugin I can use pytest --suppress-tests-failed-exit-code to get the desired behavior.

Related

Jenkins: How to fail stage when test fails?

I have a stage in my pipeline that is running some UI tests currently this is the behaviour I get:
If the tests pass the stage goes green the next stage runs, and at
the end the build goes green.
If a test fails the stage goes green, the next stage runs, and at the
end the build is yellow (unstable)
How can I make it so that instead of moving on to the next stage if a test fails the pipeline is failed?
This is the stage of my pipeline, I've tried adding a post section but even when a test fails it reports success.
stage('UITests') {
steps {
withCredentials([file(credentialsId: 'env_file', variable: 'envFile')]) {
sh '''
cat $envFile > .env.dev
make run_tests
'''
}
}
// Fail build if test fail
post{
success {
echo "UI Tests passed moving to Build stage"
}
failure {
error "UI Tests Failed, stopping the build"
}}
}
In the Jenkins log for the stage I can see when a test fails I get
error Command failed with exit code 1.
This doesn't happen when a test passes so is there a reason the post block is always going to success?
The sh step returns the same status code that your actual sh command: https://www.jenkins.io/doc/pipeline/steps/workflow-durable-task-step/#sh-shell-script
You can get this status code, assign its value to a variable and then fail with a custom message:
def statusCode = sh script:"cat $envFile > .env.dev && make run_tests", returnStatus:true
if (statusCode != 0) {
error 'UI Tests Failed, stopping the build'
}

How to run a task if tests fail in Jenkins

I have a site in production. And I have a simple playwright test that browses to the site and does some basic checks to make sure that it's up.
I'd like to have this job running in Jenkins every 5 minutes, and if the tests fail I want to run a script that will restart the production server. If the tests pass, I don't want to do anything.
What's the easiest way of doing this?
I have the MultiJob plugin that I thought I could use, and have the restart triggered on the failed test step, but it doesn't seem to have the ability to trigger specifically on fail.
Something like the following will do the Job for you. I'm assuming you have a second Job that will take care of the restart.
pipeline {
agent any
triggers{
cron('*/5 * * * *')
}
stages {
stage("Run the Test") {
steps{
echo "Running the Test"
// I'm returning exit code 1 so jenkins will think this failed
sh '''
echo "RUN SOMETHING"
exit 1
'''
}
}
}
post {
success {
echo "Success: Do nothing"
}
failure {
echo 'I failed :(, Execute restart Job'
// Executing the restart Job.
build job: 'RestartJob'
}
}
}

Jenkins executes second command with sh using docker.image.withRun

I currently have a Jenkins script which starts a Docker container in which the Selenium tests are run using Maven. The Selenium tests are executed successfully, and Maven returns "Build Success".
The problem is as following: Instead of only executing the sh command specified in the Jenkinsfile, Jenkins also executes an unknown second sh command.
Jenkins Pipeline Step
As shown in the image, the highlighted part is executed as command, which obviously is not a command, meaning that the Docker container returns error code 127.
Jenkinsfile:
node {
stage('Checkout Code') {
checkout scm
}
try {
withEnv(["JAVA_HOME=${tool 'JDK 11.0'}", "PATH+MAVEN=${tool 'apache-maven-3.x'}/bin", "PATH+JAVA=${env.JAVA_HOME}/bin"]) {
stage('Run Selenide Tests') {
docker.image('selenium/standalone-chrome').withRun('-v /dev/shm:/dev/shm -P') { c->
sh "mvn clean test -Denvironment=${env.Profile} -Dselenide.headless=true -Dselenide.remote=http://" + c.port(4444) + "/wd/hub"
}
}
}
}catch(e){
currentBuild.result = "FAILURE"
throw e
} finally {
stage('Notify Slack Channel of Tests status') {
// Hidden
}
}
}
Console Output (some parts hidden because not relevant):
+ docker run -d -v /dev/shm:/dev/shm -P selenium/standalone-chrome
+ docker port a15967ce0efbda908f6ba9bb7c8c633bb64e54a6557e5c23097ea47ed0540ff9 4444
+ mvn clean test -Denvironment=jenkins -Dselenide.headless=true -Dselenide.remote=http://0.0.0.0:49827
// Maven tests
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:14 min
[INFO] Finished at: 2022-04-14T15:36:38+02:00
[INFO] ------------------------------------------------------------------------
+ :::49821/wd/hub
/var/lib/jenkins/workspace/selenide-tests/test#tmp/durable-58ae7b8f/script.sh: 2:
/var/lib/jenkins/workspace/selenide-tests/test#tmp/durable-58ae7b8f/script.sh: :::49821/wd/hub: not found
+ docker stop a15967ce0efbda908f6ba9bb7c8c633bb64e54a6557e5c23097ea47ed0540ff9
a15967ce0efbda908f6ba9bb7c8c633bb64e54a6557e5c23097ea47ed0540ff9
+ docker rm -f a15967ce0efbda908f6ba9bb7c8c633bb64e54a6557e5c23097ea47ed0540ff9
a15967ce0efbda908f6ba9bb7c8c633bb64e54a6557e5c23097ea47ed0540ff9
ERROR: script returned exit code 127
Finished: FAILURE
Is this a common issue which is easily solvable, or is something wrong with my Jenkinsfile and how can I fix this?
Thanks
It seems the /wd/hub part comes from you executed line of code, which leads me to believe that your problem is due to the way you have added quotes.
Your line of code is:
sh "mvn clean test -Denvironment=${env.Profile} -Dselenide.headless=true -Dselenide.remote=http://" + c.port(4444) + "/wd/hub"
Specifically, you open you command with ", then close it after http:// with another ". I'm guessing Jenkins doesn't find this acceptable. Try creating the url separately
def url = "http://" + c.port(4444) + "/wd/hub"
and simply using this variable in your executing line
sh "mvn clean test -Denvironment=${env.Profile} -Dselenide.headless=true -Dselenide.remote=${url}"
I haven't used docker.image before so you might have to play around a bit to get this working.
After some more digging around the documentation and trying different stuff out, this is what worked for me:
node {
stage('Checkout Code') {
checkout scm
}
try {
withEnv(["JAVA_HOME=${tool 'JDK 11.0'}", "PATH+MAVEN=${tool 'apache-maven-3.x'}/bin", "PATH+JAVA=${env.JAVA_HOME}/bin"]) {
stage('Run Selenide Tests') {
docker.image('selenium/standalone-chrome').withRun('-v /dev/shm:/dev/shm -p 4444:4444') {
def selenideRemote = "http://0.0.0.0:4444/wd/hub"
sh "mvn clean test -Denvironment=${env.Profile} -Dselenide.headless=true -Dselenide.remote=${selenideRemote}
}
}
}
}catch(e){
currentBuild.result = "FAILURE"
throw e
} finally {
stage('Notify Slack Channel of Tests status') {
// Hidden
}
}
}
So I replaced .withRun('-v /dev/shm:/dev/shm -P') with .withRun(-v /dev/shm:/dev/shm -p 4444:4444') and replaced c.port(4444) with the selenideRemote variable.
Removing c.port(4444) stopped executing the second command in question. Replacing -P with -p 4444:4444 prevented port 4444 from inside the container to be assigned to a random port on the host, which also prevented the usage of c.port(4444).

terraform fmt fails within a Jenkins pipeline

I am running
sh "terraform fmt -list=true -write=false -diff=true -check=true"
within a Jenkins pipeline, and got the following weird error message:
ERROR: Terraform Lint failed due to: hudson.AbortException: script returned exit code 3
Finished: FAILURE
If I just run the terraform fmt ... without the pipeline, then it is ok. Any suggestion on how I can fix this weird error?
As mentioned in the Release Notes for Terraform 0.10.5 (but not in the documentation), the -check parameter
makes it return a non-zero exit status if any formatting changes are required
When you run it locally, it will not output any error, but if you would afterwards check the exit code echo $?, it would be again 3.
In Jenkins, however, any sh step returning non-zero status is treated as failure. So you either remove the -check option, or you deal with it correctly, i.e.,
def fmtStatus = sh "terraform fmt -list=true -write=false -diff=true -check=true"
if (fmtStatus == 0) {
echo "All correct"
} else {
echo "Changes necessary"
}

Jenkins build gets failed, when unit test cases are failed

Jenkins build gets failed, when unit test cases are failed,
Here am using pipeline script in jenkins, Need to generate HTML report using mocha-awesome, I can get the HTML report only when all test cases are passed, Build fails if any functions failed in my testcases.Here you can see the screenshot
The call to the unit test run returns with exit status 1.
What you can do is use the returnStatus option for the sh or bat step which will then not fail the build itself but leaves that up to you:
def exitStatus = sh returnStatus: true, script: 'unittests'
or:
def exitStatus = bat returnStatus: true, script: 'unittests.exe'
Having this you can selectively fail the build, i.e. if exitStatus == 1 then ignore as it signals a test fail, if it is anything but 0 or 1 then fail the build using the error step.

Resources