Jenkins shared libraries: tested

Jenkins is a very neat tool to implement a continuous delivery process, mainly due to its flexibility. Sometimes it can be hard though to keep complexity low, and when that happens, (automated) tests become far more important.

Jenkins should in fact be running tests that verify the scripts running tests, proving they actually work. Warning: this dog will chase its own tail.

Jenkins pipeline scripts

Once, not so long ago, the way to go was to manage jobs manually using the GUI. At the moment Cloudbees is proposing pipelines as scripts, either scripted procedurally or declarative.

Declarative pipelines are still in development. Their primary intent is to be suitable to be edited from a simple GUI.

Extending Jenkins scripting

In the world of open source, integration is becoming the only major thing a company needs to implement themselves.

Using just a single script however will get you only so far. At a certain point you will want to introduce abstractions.

The Jenkins Shared library

Cloudbees recently introduced a way of having more than just a script, but keeping things relatively easy and secure to use.

The ‘shared library’ is a Jenkins concept that allows projects to declaratively import a full repository of classes and scripts.

In short, any shared library will have a few common folders:

1

2

3

4

5

/--Jenkins will load the complete repository,the following places are important:

/src--classes,optionally using@Grab toget dependencies

/vars--scripts,closures,dsl building

/resources--anything else,stuff

It comes with tests

As with all new areas of applying code, once maturity is reached, tests become relevant. Fortunately there is a way to run tests for the pipeline code, be it scripts or (typed) classes.

These tests will lean towards unit tests even though the unit boundaries for pipelines are not that clear. Interaction with external systems is of course not something best covered using unit tests. The following examples are aimed primarily at testing groovy compilation, execution and (emulated) interpretation by Jenkins. When defining clear units (classes), these can become proper targets for unit tests once more.

JUnit example

Note that the code is groovy, but it can just be java, just add semi colons 😉 (okay maybe a few anonymous classes etc, but should be possible).

Project layout

To be able to work with a shared library, a project setup would be convenient, so why not use groovy through gradle? The layout of a Jenkins shared library is not standard for gradle. This means that a little config is needed.

Once the configuration is in place, the project will build quite easily. There are some snags though, which will show in the gradle config file below. The little problems together make the build.gradle file not quite as trivial.

Gradle config

Check the following build.gradle config, there are hints on possible difficulties in the comments.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

// Apply the java plugin to add support for Java

apply plugin:'java'

apply plugin:'groovy'

apply plugin:'eclipse'

apply plugin:'idea'

apply plugin:'project-report'

targetCompatibility='1.8'

sourceCompatibility='1.8'

model{

components{

main(JvmLibrarySpec){

targetPlatform'java8'

}

}

}

// follow the structure as dictated by Jenkins:

sourceSets{

main{

groovy{

srcDirs=['src','vars']

}

resources{

srcDirs=['resources']

}

}

test{

groovy{

srcDirs=['test']

}

}

}

// allow for the (pipeline code) Ivy Grab/grape system to do some setup...

Stringhome=System.getProperty("user.home")

task intializeGrapeConfig(type:Copy){

doFirst{

println"installing grape config in: ${home}/.groovy"

}

from'.'

include"grapeConfig.xml"// assuming that this file is in the shared library!

into"${home}/.groovy/"

}

intializeGrapeConfig.group="build setup"

// In this section you declare where to find the dependencies of your project

repositories{

// Use 'jcenter' for resolving your dependencies.

// You can declare any Maven/Ivy/file repository here.

mavenCentral()

jcenter()

maven{

url"http://repo.jenkins-ci.org/releases/"

}

}

// In this section you declare the dependencies for your production and test code

dependencies{

// to be sure @Grab compile time dependency downloading works in scripts, have the goods ready

compile group:'org.codehaus.groovy',name:'groovy-all',version:'2.4.9'

compile group:'org.apache.ivy',name:'ivy',version:'2.4.0'

// The production code uses the SLF4J logging API at compile time

compile'org.slf4j:slf4j-api:1.7.21'

// Declare the dependency for your favourite test framework you want to use in your tests.

More info

Chasing tails

The recently developed option to test pipeline code is great. As with all testing options though it’s important to keep in mind whether the extra testing effort is worth it.

Having Jenkins run tests on test code that runs tests seems kind of pointless, after all tests will (hopefully) fail both when the production code is broken and when the tests are broken. It turns out that having some trivial unit tests for pipeline code does deliver a lot of value by creating a very short feedback loop. In terms of knowing whether the pipeline actually does the ‘right’ thing is a different matter. Tests that can assert this are far more complicated, true system tests are probably more suitable for that purpose.