Initial check-in of lesfurets test improvements (#23)

Adding lesfurets test framwork improvements via helper classes
This commit is contained in:
Sven Merk
2018-01-10 10:27:55 +01:00
committed by GitHub
parent 2a09b9bfc1
commit fe89155a04
12 changed files with 469 additions and 172 deletions
+19 -2
View File
@@ -46,6 +46,12 @@
<dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>junit</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-aggregator</artifactId>
@@ -84,7 +90,19 @@
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<!-- any version of Groovy \>= 1.5.0 should work here -->
<version>2.4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.yaml/snakeyaml -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
@@ -108,7 +126,6 @@
</goals>
<configuration>
<sources>
<source>test/java</source>
<source>test/groovy</source>
</sources>
</configuration>
@@ -0,0 +1,76 @@
package com.sap.piper
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.JenkinsLoggingRule
import util.JenkinsSetupRule
import util.SharedLibraryCreator
import static org.hamcrest.Matchers.containsString
import static org.hamcrest.Matchers.hasSize
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertThat
class UtilsTest extends BasePipelineTest {
@Rule
public ExpectedException exception = ExpectedException.none()
@Rule
public JenkinsSetupRule setUpRule = new JenkinsSetupRule(this, SharedLibraryCreator.lazyLoadedLibrary)
Utils utils
@Before
void init() throws Exception {
utils = new Utils()
prepareObjectInterceptors(utils)
}
void prepareObjectInterceptors(object) {
object.metaClass.invokeMethod = helper.getMethodInterceptor()
object.metaClass.static.invokeMethod = helper.getMethodInterceptor()
object.metaClass.methodMissing = helper.getMethodMissingInterceptor()
}
@Test
void testGetMandatoryParameterValid() {
def sourceMap = [test1: 'value1', test2: 'value2']
def defaultFallbackMap = [myDefault1: 'default1']
assertEquals('value1', utils.getMandatoryParameter(sourceMap, 'test1', null))
assertEquals('value1', utils.getMandatoryParameter(sourceMap, 'test1', ''))
assertEquals('value1', utils.getMandatoryParameter(sourceMap, 'test1', 'customValue'))
}
@Test
void testGetMandatoryParameterDefaultFallback() {
def myMap = [test1: 'value1', test2: 'value2']
assertEquals('', utils.getMandatoryParameter(myMap, 'test3', ''))
assertEquals('customValue', utils.getMandatoryParameter(myMap, 'test3', 'customValue'))
}
@Test
void testGetMandatoryParameterFail() {
def myMap = [test1: 'value1', test2: 'value2']
exception.expect(Exception.class)
exception.expectMessage("ERROR - NO VALUE AVAILABLE FOR")
utils.getMandatoryParameter(myMap, 'test3', null)
}
}
@@ -0,0 +1,37 @@
package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class JenkinsLoggingRule implements TestRule {
final BasePipelineTest testInstance
String log = ""
JenkinsLoggingRule(BasePipelineTest testInstance) {
this.testInstance = testInstance
}
@Override
Statement apply(Statement base, Description description) {
return statement(base)
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
void evaluate() throws Throwable {
testInstance.helper.registerAllowedMethod("echo", [String.class], {
echoInput ->
log += "$echoInput \n"
})
base.evaluate()
}
}
}
}
@@ -0,0 +1,55 @@
package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class JenkinsPropertiesRule implements TestRule {
final BasePipelineTest testInstance
final String propertyPath
final Properties configProperties
JenkinsPropertiesRule(BasePipelineTest testInstance, String propertyPath) {
this.testInstance = testInstance
this.propertyPath = propertyPath
configProperties = loadProperties(propertyPath)
}
@Override
Statement apply(Statement base, Description description) {
return statement(base)
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
void evaluate() throws Throwable {
testInstance.helper.registerAllowedMethod("readProperties", [Map.class], {
propertyPath ->
if (JenkinsPropertiesRule.this.propertyPath.contains(propertyPath.file)) {
return JenkinsPropertiesRule.this.configProperties
}
throw new Exception("Could not find the properties with path $propertyPath")
})
base.evaluate()
}
}
}
static Properties loadProperties(String path) {
def inputStream = new File(path).newInputStream()
def properties = new Properties()
properties.load(inputStream)
inputStream.close()
return properties
}
}
@@ -0,0 +1,38 @@
package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class JenkinsScriptLoaderRule implements TestRule {
final BasePipelineTest testInstance
final String scriptBasePath
JenkinsScriptLoaderRule(BasePipelineTest testInstance, String scriptBasePath) {
this.testInstance = testInstance
this.scriptBasePath = scriptBasePath
}
@Override
Statement apply(Statement base, Description description) {
return statement(base)
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
void evaluate() throws Throwable {
testInstance.helper.registerAllowedMethod("load", [String.class], {
fileNameIntegration ->
return testInstance.loadScript("$scriptBasePath/$fileNameIntegration")
})
base.evaluate()
}
}
}
}
+51
View File
@@ -0,0 +1,51 @@
package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class JenkinsSetupRule implements TestRule {
def library = SharedLibraryCreator.implicitLoadedLibrary
final BasePipelineTest testInstance
JenkinsSetupRule(BasePipelineTest testInstance) {
this.testInstance = testInstance
}
JenkinsSetupRule(BasePipelineTest testInstance, LibraryConfiguration configuration) {
this.testInstance = testInstance
this.library = configuration
}
@Override
Statement apply(Statement base, Description description) {
return statement(base)
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
void evaluate() throws Throwable {
testInstance.scriptRoots += "vars/"
testInstance.setUp()
// register library
testInstance.helper.registerSharedLibrary(library)
// set jenkins job mock variables
testInstance.binding.setVariable('env', [
JOB_NAME : 'p',
BUILD_NUMBER: '1',
BUILD_URL : ''
])
base.evaluate()
testInstance.printCallStack()
}
}
}
}
+145
View File
@@ -0,0 +1,145 @@
#!groovy
package util
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import hudson.tasks.junit.TestResult
import org.yaml.snakeyaml.Yaml
/**
* This is a Helper class for mocking.
*
* It can be used to load test data or to mock Jenkins or Maven specific objects.
**/
class MockHelper {
/**
* load properties from resources for mocking return value of readProperties method
* @param path to properties
* @return properties file
*/
Properties loadProperties( String path ){
Properties p = new Properties()
File pFile = new File( path )
p.load( pFile.newDataInputStream() )
return p
}
/**
* load JSON from resources for mocking return value of readJSON method
* @param path to json file
* @return json file
*/
Object loadJSON( String path ){
def js = new JsonSlurper()
def reader = new BufferedReader(new FileReader( path ))
def j = js.parse(reader)
return j
}
/**
* load YAML from resources for mocking return value of readYaml method
* @param path to yaml file
* @return yaml file
*/
Object loadYAML( String path ){
return new Yaml().load(new FileReader(path))
}
/**
* creates HTTP response for mocking return value of httpRequest method
* @param text - text to parse into json object
* @return json Object
*/
Object createResponse( String text ){
def response = new JsonBuilder(new JsonSlurper().parseText( text ))
return response
}
/**
* load File from resources for mocking return value of readFile method
* @param path to file
* @return File
*/
File loadFile( String path ){
return new File( path )
}
/**
* load POM from resources for mocking return value of readMavenPom method
* @param path to pom file
* @return Pom class
*/
MockPom loadPom(String path ){
return new MockPom( path )
}
/**
* Inner class to mock maven descriptor
*/
class MockPom {
def f
def pom
MockPom(String path){
this.f = new File( path )
if ( f.exists() ){
this.pom = new XmlSlurper().parse(f)
}
else {
throw new FileNotFoundException( 'Failed to find file: ' + path )
}
}
String getVersion(){
return pom.version
}
String getGroupId(){
return pom.groupId
}
String getArtifactId(){
return pom.artifactId
}
String getPackaging(){
return pom.packaging
}
String getName(){
return pom.name
}
}
MockBuild loadMockBuild(){
return new MockBuild()
}
MockBuild loadMockBuild(TestResult result){
return new MockBuild(result)
}
/**
* Inner class to mock Jenkins' currentBuild return object in scripts
*/
class MockBuild {
TestResult testResult
MockBuild(){}
MockBuild(TestResult result){
testResult = result
}
MockLibrary getAction(Class c){
println("MockLibrary -> getAction - arg: " + c.toString() )
return new MockLibrary()
}
class MockLibrary {
MockLibrary(){}
// return default
List getLibraries(){
println("MockLibrary -> getLibraries")
return [ [name: 'default-library', version: 'default-master', trusted: true] ]
}
TestResult getResult() {
println("MockLibrary -> getResult")
return testResult
}
}
}
}
+24
View File
@@ -0,0 +1,24 @@
#!groovy
package util
import com.lesfurets.jenkins.unit.global.lib.SourceRetriever
/**
* Retrieves the shared lib sources of the current project which are expected to be
* at the default location &quot;./vars&quot;.
*/
class ProjectSource implements SourceRetriever {
private def sourceDir = new File('.')
/*
* None of the parameters provided in the signature are used in the use-case of that retriever.
*/
List<URL> retrieve(String repository, String branch, String targetPath) {
if (sourceDir.exists()) {
return [sourceDir.toURI().toURL()]
}
throw new IllegalStateException("Directory $sourceDir.path does not exists!")
}
}
@@ -0,0 +1,24 @@
package util
import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
class SharedLibraryCreator {
static def lazyLoadedLibrary = library()
.name('piper-library')
.retriever(new ProjectSource())
.targetPath('is/not/necessary')
.defaultVersion("master")
.allowOverride(true)
.implicit(false)
.build()
static def implicitLoadedLibrary = library()
.name('piper-library')
.retriever(new ProjectSource())
.targetPath('is/not/necessary')
.defaultVersion("master")
.allowOverride(true)
.implicit(true)
.build()
}
-39
View File
@@ -1,39 +0,0 @@
import java.io.File;
import java.io.IOException;
import javax.inject.Inject;
import jenkins.model.Jenkins;
import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariableList;
import org.jenkinsci.plugins.workflow.cps.global.WorkflowLibRepository;
import org.junit.ClassRule;
import org.junit.Rule;
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.RestartableJenkinsRule;
public class AbstractJenkinsTest {
@ClassRule
public static BuildWatcher buildWatcher = new BuildWatcher();
@Rule
public RestartableJenkinsRule story = new RestartableJenkinsRule();
@Inject
protected Jenkins jenkins;
@Inject
WorkflowLibRepository repo;
@Inject
protected UserDefinedGlobalVariableList uvl;
public AbstractJenkinsTest() {
super();
}
protected void copyLibrarySources() {
try {
FileUtils.copyDirectory(new File("vars"), new File(repo.workspace, "vars"));
FileUtils.copyDirectory(new File("src"), new File(repo.workspace, "src"));
FileUtils.copyDirectory(new File("resources"), new File(repo.workspace, "resources"));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
-91
View File
@@ -1,91 +0,0 @@
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.Test;
import org.junit.runners.model.Statement;
import java.io.File;
import java.util.Arrays;
public class AcmeTest extends AbstractJenkinsTest {
/**
* Test acme getter and setter
*/
@Test
public void acmeTest() throws Exception {
story.addStep(new Statement() {
@Override public void evaluate() throws Throwable {
//File vars = new File(repo.workspace, UserDefinedGlobalVariable.PREFIX);
File vars = new File(repo.workspace, "vars");
vars.mkdirs();
FileUtils.writeStringToFile(new File(vars, "acme.groovy"), StringUtils.join(Arrays.asList(
"class acme implements Serializable {",
" private String name = 'initial'",
" def setName(value) {",
" this.name = value",
" }",
" def getName() {",
" this.name",
" }",
" def caution(message) {",
" echo \"Hello, ${name}! CAUTION: ${message}\"",
" }",
"}")
, "\n"));
// simulate the effect of push
uvl.rebuild();
WorkflowJob p = jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"node {\n" +
"acme.setName('acmeName')\n"+
"echo acme.getName()\n" +
"}",
true));
// build this workflow
WorkflowRun b = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0));
story.j.assertLogContains("acmeName", b);
}
});
}
//@Test
public void acmeTest2() throws Exception {
story.addStep(new Statement() {
@Override
public void evaluate() throws Throwable {
copyLibrarySources();
// simulate the effect of push
uvl.rebuild();
WorkflowJob p = jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"import com.sap.piper.Utils\n" +
"node {\n" +
"acme.setName('myName')\n"+
"assert acme.getName() == 'myName'\n" +
"}",
true));
// build this workflow
WorkflowRun b = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0));
}
});
}
}
-40
View File
@@ -1,40 +0,0 @@
import java.io.File;
import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.runners.model.Statement;
public class _TemplateTest extends AbstractJenkinsTest {
/**
* Test ... step
*/
//@Test
public void testWhatEver() throws Exception {
story.addStep(new Statement() {
@Override
public void evaluate() throws Throwable {
copyLibrarySources();
// simulate the effect of push
uvl.rebuild();
WorkflowJob p = jenkins.createProject(WorkflowJob.class, "p");
//copy test resources into workspace
FileUtils.copyDirectory(new File("test/resources"), new File(jenkins.getWorkspaceFor(p).getRemote(), "resources"));
p.setDefinition(new CpsFlowDefinition(
"node {\n" +
"\n" +
"\n" +
"}",
true)
);
// build this workflow
WorkflowRun b = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0));
//story.j.assertLogContains("this is part of the log", b);
}
});
}
}