I’m trying to write unit test to my Jenkins shared library using jenkins-pipeline-unit but for no success.
My project/directory structure is:
src/
└── org
└── company
├── JobData.groovy
├── Constants.groovy
├── Logger.groovy
└── Utils.groovy
vars
└── automationTest.groovy
└── fileIsEmpty.groovy
test
└── AutomationTestTest.groovy
I’ve initialized gradle and created some build.gradle file with many dependencies and tried to be accurate and use the plugins versions from my jenkins instance.
What I’m trying to do is to write test for automationTest.groovy variable.
here is some relevant code:
//automationTest.groovy
import org.company.Constants
import org.company.Utils
import org.company.JobData
import org.company.Logger
import java.nio.file.NoSuchFileException
def beafBAtest(String setupName, String suiteFile){
stage("BA_Test_Beaf"){
env.FAILED_STAGE = env.STAGE_NAME
Logger.colorPrint(this, " Processing Beaf BA Test ".center(61, '='))
String logLevel = "debug"
sh label: "Pytest Execution (BA)", script: """#!/bin/bash
#Create and activate virtualenv
#python3.6 -m venv venv
virtualenv --python=python3 venv
source venv/bin/activate
"""
logFiles = sh(returnStdout: true, script: "ls -1 pytests/log_*.ba || true", label: "BA log parsing").split()
for(logFile in logFiles){
if(fileIsEmpty(logFile))
continue
def logContent = readFile file: "$logFile"
try{
List testsSummariesRows = Utils.getTestLogRows(logContent.readLines(), 1)
if(testsSummariesRows.isEmpty()){
Logger.printError(this, "Error")
}
testsSummariesRows.each{ testsSummaryRow ->
def currSummary = readJSON(text: testsSummaryRow)
currSummary.each{ testCaseName, testCaseInfo ->
JobData.instance.BAtests[testCaseName] = (JobData.instance.BAtests[testCaseName] ?: []) +
[
"link" : testCaseInfo["TestReportRunUrl"] ?: "n/a",
"result" : testCaseInfo["Verdict"] ?: "n/a"
]
}
}
}catch(e){
Logger.printError(this, "Error2")
}
}
sh(label: "BA test BEAF result", script: "exit \$(cat pytests/exit_status)")
}
}
I need to test the value of the singleton JobData within the test file but have no idea how to import it
//Utils.groovy
package org.company
import groovy.json.JsonOutput
import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.Yaml
import org.apache.http.client.utils.URIBuilder;
import org.company.Constants
import java.text.SimpleDateFormat;
import java.util.Date;
import hudson.model.User
import hudson.tasks.Mailer
import jenkins.model.Jenkins
import org.jenkinsci.plugins.pipeline.modeldefinition.Utils as jUtils
import org.company.Logger
import org.company.JobData
class Utils{
// A lot of static classes which uses the imports (like jenkins.model.Jenkins)
static def yamlToString(Map yaml){
//implementation here
}
static List getTestLogRows(List loglines, int lineOffset = 0){
}
//... (more methods)
}
//JobData.groovy
package org.company
import org.company.Constants
import org.company.Utils
@Singleton
public class JobData{
//singleton for storing job data
public String releaseBranch
public def BAtests = [:]
...
Everytime I run the test and getting undefined classes I struggled with it a lot and the current exceptions is:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during conversion: java.lang.NoClassDefFoundError: Unable to load class jenkins.model.Jenkins due to missing dependency antlr/ANTLRException
This is the dependencies block of my build.gradle
:
dependencies {
implementation "org.codehaus.groovy:groovy-all:2.4.12"
implementation 'org.codehaus.groovy:groovy-jaxb:3.0.17'
implementation 'javax.servlet:javax.servlet-api:4.0.1@jar'
implementation 'org.jenkins-ci.main:remoting:3.40@jar'
implementation 'org.springframework.security:spring-security-core:5.5.2'
implementation 'com.thoughtworks.xstream:xstream:1.4.16'
implementation 'org.antlr:antlr-runtime:3.5.2@jar'
implementation 'antlr:antlr:3.0ea8'
implementation 'org.kohsuke.stapler:stapler:1777.v7c6fe6d54a_0c@jar'
// testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0'
testImplementation "com.lesfurets:jenkins-pipeline-unit:1.9"
testImplementation "junit:junit:4.12"
//Fetch 3rd imports for src/utils class
testImplementation 'org.yaml:snakeyaml:1.28'
testImplementation 'org.apache.httpcomponents:httpclient:4.5.13@jar'
testImplementation 'org.jenkins-ci.main:jenkins-core:2.303.2@jar'
testImplementation 'org.jenkinsci.plugins:pipeline-model-api:1.9.2@jar'
testImplementation 'org.jenkinsci.plugins:pipeline-model-definition:1.9.2@jar'
testImplementation 'org.jenkinsci.plugins:pipeline-model-extensions:1.9.2@jar'
implementation 'org.jenkinsci.plugins:pipeline-model-declarative-agent:1.1@jar'
testImplementation 'org.connectbot.jbcrypt:jbcrypt:1.0.0@jar'
testImplementation 'org.jenkins-ci.plugins:mailer:408.vd726a_1130320@jar'
}
And this is the test that I’m trying to write:
//test/AutomationTestTest.groovy
//Should I?
//package org.company
//import org.company.JobData
import org.junit.*
import com.lesfurets.jenkins.unit.*
import static groovy.test.GroovyAssert.*
//Imports for loading the library itself
import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
import static com.lesfurets.jenkins.unit.global.lib.ProjectSource.projectSource
class AutomationTestTest extends BasePipelineTest {
Object script
def automationTest
def resourcesTestDir
@Before
void setUp(){
super.setUp()
//This fails due to unsatisfied dependencies in Utils.groovy
Object library = library()
.name('jenkins-shared-lib')
.defaultVersion('<notNeeded>')
.allowOverride(true)
.implicit(true)
.targetPath('<notNeeded>')
.retriever(projectSource())
.build()
helper.registerSharedLibrary(library)
script = loadScript('test/resources/EmptyPipeline.groovy')
assertNotNull(script)
resourcesTestDir = new File('test/resources/AutomationTest')
automationTest = loadScript("vars/automationTest.groovy")
}
@Test
void testBeafBAtest(){
helper.addShMock(~'''(?m)#!/bin/bash
#Create and activate virtualenv
#python3.6 -m venv venv
.*
''', '', 0)
helper.addShMock('ls -1 pytests/log_*.ba || true', baLogs.join('\n') , 0)
helper.addShMock('exit \$(cat pytests/exit_status)', '', 0)
//Mock log files from test/resources/AutomationTest such as ['log_0.ba', 'log_1.ba']
resourcesTestDir.eachFileMatch(~/log_\d+\.ba/){ baLogFile ->
helper.addReadFileMock(baLogFile.name, baLogFile.text)
}
automationTest.beafBAtest("HVS180", "some-test-file")
//How should I verify that JObData.instance.BAtests has the correct value
}
My questions are:
- How can I manage to run the test pass the missing dependencies such as
missing dependency antlr/ANTLRException
, is there any chance to fill gradle dependencies automatically? - Am I doing something wrong? how the test should be done? how should I check the singleton content at the end of the test?, should I put the test inside
package org.company
?