You are on page 1of 60

DEGREE PROJECT, IN COMPUTER SCIENCE , SECOND LEVEL

STOCKHOLM, SWEDEN 2015

Test-Driven Development in Clojure


AN ANALYSIS OF HOW DIFFERENCES
BETWEEN CLOJURE AND JAVA AFFECTS
UNIT TESTING AND DESIGN PATTERNS
NICLAS NILSSON

KTH ROYAL INSTITUTE OF TECHNOLOGY

SCHOOL OF COMPUTER SCIENCE AND COMMUNICATION (CSC)


Test-Driven Development in Clojure
(Testdriven utveckling i Clojure)

An analysis of how differences between Clojure and Java affects unit testing and
design patterns
(En analys av hur skillnader mellan Clojure och Java påverkar enhetstestning och
designmönster)

NICLAS NILSSON

Master’s Thesis in Computer Science at CSC



E-mail: ninilss@kth.se
Supervisor: Anders Lansner
Examiner: Jens Lagergren

March 2015
Abstract
Agile methods and Test-Driven Development are well es-
tablished methodologies within the software development
industry. As a large part of today’s software development is
done in Object-Oriented languages like Java it’s only nat-
ural that agile best practices have evolved to fit into the
Object-Oriented paradigm. Clojure is a relatively young
programming language that greatly differs from Object-
Oriented languages and it’s thus not certain that these best
practices can be directly applicable in Clojure development.
This thesis attempts to identify key differences between
Clojure and Java that may affect unit testing and the Test-
Driven Development process. It finds that although the
two languages are fundamentally the difference between
them in regards to unit test creation and execution is small.
The most striking consequence of Clojure’s lack of Object-
Orientation is that dependency injection must be done be-
tween functions rather than between classes and objects.
It’s also argued that the relative immaturity of the avail-
able Clojure development tools can have negative effects on
the Test-Driven Development process.
Referat
Agila metoder och Testdriven Utveckling är väletablerade
metoder inom mjukvaruindustrin. Då en stor del av dagens
mjukvaruutveckling sker inom Objektorienterade språk som
Java är det bara naturligt att agila best-practices har ut-
vecklats för att passa den Objektorienterade paradigmen.
Clojure är ett relativt ungt programmeringsspråk som skil-
jer sig kraftigt från Objektorienterade språk och det är där-
för inte självklart att dessa best-practices kan vara direkt
applicerbara på utveckling i Clojure.
Denna uppsats försöker identifiera de huvudskillnader
mellan Clojure och Java som kan ha en direkt påverkan
på enhetstestning och den Testdrivna Utvecklingsproces-
sen. Man upptäcker att det - de två språkens fundamentala
skillnader till trots - endast finns små skillnader som på-
verkar skapandet och körandet av enhetstester. Den mest
iögonfallande konsekvensen av bristen på Objektorientering
i Clojure är att Dependency Injection måste ske på funk-
tionsnivå, istället för klass- och objektsnivå. Man argumen-
terar även för att den relativa omogenheten hos verktyg
som används vid mjukvaruutveckling i Clojure kan ha en
negativ effekt på den Testdrivna Utvecklingsprocessen.
Glossary

CD Continuous Deployment. The process of often releasing a soft-


ware product into its production environment, or at least an
environment which is similar.
CI Continuous Integration. The process of often merging source
code between developers during development.

IDE Integrated Development Environment. A tool used in software


development that often provides an editor, build automation,
debuggers and other useful tools.

JVM Java Virtual Machine. A virtual machine that executes Java


bytecode on many host platforms. Most Java bytecode run on
the JVM has been compiled from higher level programming
languages, such as Java or Clojure.

OO Object-Oriented. A programming paradigm where values


and functions are coupled as objects that are instances of
template-like classes.

REPL Read-Eval-Print Loop. An interactive prompt that takes pro-


gramming expressions as input, evaluates them and presents
the result to the user. Useful in exploring programming envi-
ronments.

SOLID SOLID. A group of design principles in Object-Oriented pro-


gramming languages. Short for Single responsibility, Open-
closed, Liskov substitution, Interface segregation and Depen-
dency inversion.

TDD Test-Driven Development. An iterative software development


principle in which tests are written before any production
code.
TF Test-First. The practice of writing software tests before the
production code that it tests has been written.
TL Test-Last. The practice of writing software tests after the
production code that it tests has been written.

XP Extreme Programming. An agile software development


methodology centered around developers that intends to im-
prove quality by responding to change.
Contents

1 Introduction 1
1.1 Enter Clojure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Problem statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.1 Scope and limitations . . . . . . . . . . . . . . . . . . . . . . 3

2 Background 5
2.1 The agile industry . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.1.1 Classical product development . . . . . . . . . . . . . . . . . 5
2.1.2 Responding to change . . . . . . . . . . . . . . . . . . . . . . 5
2.1.3 Going agile . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.1.4 Demands of the agile process . . . . . . . . . . . . . . . . . . 6
2.1.5 Sibling methods and principles . . . . . . . . . . . . . . . . . 7
2.2 Software testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.1 Classes of tests . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.2 Test automation . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Test-Driven Development . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.1 Core idea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.2 Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.3 Effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.4 Test-First vs. Test-Last . . . . . . . . . . . . . . . . . . . . . 10
2.4 Continuous Integration . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.1 Pre-conditions . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.2 Automation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.3 Continuous Deployment . . . . . . . . . . . . . . . . . . . . . 12
2.4.4 Symbiosis with Test-Driven Development (TDD) . . . . . . . 12
2.5 Design patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.5.1 Dependency injection . . . . . . . . . . . . . . . . . . . . . . 13
2.5.2 Dependency mocking . . . . . . . . . . . . . . . . . . . . . . . 14
2.5.3 Tests as documentation . . . . . . . . . . . . . . . . . . . . . 15
2.6 Clojure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.6.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.6.2 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.6.3 Concurrency . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.6.4 Resource referencing . . . . . . . . . . . . . . . . . . . . . . . 18
2.6.5 Immutable values . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.6.6 Platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.6.7 Special forms . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.6.8 Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3 Method 21
3.1 Litterature study . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2 Programming activity . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4 Results 23
4.1 How Clojure differs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.2 Testing macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.3 Dependency injection in Clojure . . . . . . . . . . . . . . . . . . . . 25
4.3.1 Each dependency as an argument . . . . . . . . . . . . . . . . 26
4.3.2 Set of dependencies as a map . . . . . . . . . . . . . . . . . . 26
4.3.3 Set of dependencies as a protocol . . . . . . . . . . . . . . . . 27
4.3.4 Dependencies as context . . . . . . . . . . . . . . . . . . . . . 28
4.4 Untestable code in namespaces . . . . . . . . . . . . . . . . . . . . . 28
4.5 Mocking in Clojure . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.5.1 Mocking injected functions . . . . . . . . . . . . . . . . . . . 29
4.5.2 Mocking non-injected functions . . . . . . . . . . . . . . . . . 30
4.5.3 Verifying function calls . . . . . . . . . . . . . . . . . . . . . . 30
4.6 Documenting Clojure . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.6.1 Expressiveness of tests in Clojure . . . . . . . . . . . . . . . . 32

5 Discussion 35
5.1 Lack of IDEs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.2 Problems of debugging . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.3 Dynamic typing and productivity . . . . . . . . . . . . . . . . . . . . 38
5.4 Mocking made easier . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.5 The REPL and automated tests . . . . . . . . . . . . . . . . . . . . . 39

6 Conclusions 41

Bibliography 43
Chapter 1

Introduction

The computer industry today is an exciting industry to be a part of. The develop-
ment of new technology is happening at record speeds with new devices, interfaces
and ways of interacting with the world quickly becoming essential parts of our every
day lives at a rate that can only be described as revolutionary.
Along with this increase in the amount of computer products present on the
market comes the increased need for talented developers who can quickly create
stable and maintainable software that satiates the markets needs for quality prod-
ucts. This pressure of shortening the time between a product’s conception to its
release becomes problematic when practicing the old waterfall-style of development
that tends to favor large scale, monolithic software within large timeframes and
budgets. Couple this with the improvements in technology that allows development
teams to release their products with the click of a button and it’s no wonder that
agile methods have become so prevalent.
As more and more teams adopt agile methods in their every day work the pitfalls
of the iterative development process become ever more apparent. When products
continually grows and changes so does also the need for testing. It’s not longer
practically possible to postpone the product’s testing phase towards the end of its
development. As the rate of development changes, so must also the rate of its
testing. For the development process to be stable over time without a dramatic
increase in technical debt the testing of a product must be as continuous as its
increase in functionality. The longevity of the development process has at times
been described as:

“Software development is a marathon, not a sprint” - Unknown

A development method that began to emerge around the same time as agile meth-
ods were formulated was the method of TDD. This method embraces the testing
part of software development by not viewing tests as simply a means of verifying
program behavior but instead considering them to be the key driving force behind
the software’s development and design. TDD depends on the presumption that
tests are quick to execute and the development community has as a response to

1
CHAPTER 1. INTRODUCTION

this found ways of reducing the time needed to run tests through the use of de-
sign patterns. As Object-Oriented (OO) programming languages have been the
most prominent languages in the industry during the last decade [6] these design
patterns have mainly been created to fit into the OO paradigm.

1.1 Enter Clojure

The deceleration in recent years of the number of transistors that can be fit into a
single processor together with the ever increasing need for processing large data sets
has sparked the movement from single core to multicore processing. Harnessing the
power of these new generation multicore processors requires that developers pro-
duce multithreaded applications, which opens up the possibility for several kinds
of architectural problems of shared memory, thread locking etc. These problems
must often be addressed manually when developing software in popular OO pro-
gramming languages such as Java and the manual thread management can quickly
become complex.
As a response to this problem of manual thread management came the pro-
gramming language Clojure, which aims to eliminate most problems associated
with multithreading while at the same time providing a light weight alternative to
large enterprise languages such as Java.
Clojure is however a fundamentally different language from these popular OO
languages. This means that design and testing strategies that have been devel-
oped in these languages aren’t always directly applicable when developing software
in Clojure. Clojure also brings new concepts to the table that OO programmers
probably aren’t used to and thus have no experience of testing.

1.2 Motivation

The relatively young programming language Clojure is a response to problems of


software development that are likely to continue to grow in the future. The fact
that it differs in significant ways from more popular languages such as Java means
that it can be difficult to apply knowledge learned about testing in these languages
to Clojure. If one as a reasonably experienced Java developer wants to harness the
power of practicing TDD while leveraging the benefits of using Clojure one will thus
have an advantage if one knows how to apply the knowledge of practicing TDD in
Java when developing in Clojure.
The aim of this thesis is to show the difference one may encounter when transi-
tioning from Java to Clojure and to show how old knowledge can be applied in the
new agile context of Clojure development.

2
1.3. PROBLEM STATEMENT

1.3 Problem statement


• Which are the differences in available tools and language constructs between
Java and Clojure related to testing?

• How do these differences affect the creation and execution of unit tests?

• How do these differences affect the test first TDD process when developing in
Clojure?

1.3.1 Scope and limitations


This thesis will only focus on unit tests and in particular on automated testing.
Although there exists many popular OO languages today the only OO language
that will be examined closely is Java 8.
Clojure is a hosted language that have implementations on several platforms.
This thesis will only focus on the main Clojure implementation on the Java Virtual
Machine (JVM).

3
Chapter 2

Background

2.1 The agile industry


When developing software today the scopes of products often expand beyond what
is manageable by single individuals and multiple developers must thus work together
in teams. As the complexity of products increases and the need for team members to
organize themselves within teams, as well as communicating with the world outside
of the team, there naturally arises a need for the use of product development methods
that address these problems.

2.1.1 Classical product development


Back in the olden days of the 1970’s, when the software development industry
had just started to bloom, the natural choice of product development method for
software projects was usually found by gazing towards similar but well established
industries. Proven development methods in industries such as manufacturing and
construction were adopted and improved to suit the needs of the software industry.
As projects succeeded and failed it was soon realized that the software development
process could be divided into an analysis phase and a coding phase [7] - this general
idea is commonly referred to as the waterfall software development model.
Up until recently this linear style of software development has in one shape or
another been the industry norm and it has often proven to be quite effective in sup-
porting the creation of successful products. One of its arguably greater shortcoming
though has always been that that it relies heavily on initial planning and as a result
of this responds poorly to unexpected change.

2.1.2 Responding to change


It shouldn’t come as a surprise to anyone that humans are seldom capable of flaw-
lessly planning ahead; it has been argued that it’s indeed impossible to create the
perfect project plan [8]. Requirements may be incomplete, contradictory or change
and unforseen limitations and restrictions may be discovered while external pressure

5
CHAPTER 2. BACKGROUND

may be redirected. Response to these changes is hindered by the linear style of the
waterfall model, which has encouraged the search for a different method of product
development that is able to deal with critical changes. An answer came 2001 in the
form of agile software development [9].

2.1.3 Going agile


Agile methods are a family of product development methods that all have in com-
mon that they adopt an iterative model rather than a linear one. The core idea
behind all agile methods is that products should be developed incrementally in short
intervals where requirements are revised and updated continuously. By doing this
the intention is that changes in requirements are absorbed more easily in a project
when the development cycles are shorter than when they are longer, which increases
the quality of the product and in turn reduces the risk associated with unexpected
change.
The longer into the future that one has to plan the bigger is the probability
that unexpected change will occur. Agile methods’ approach to this problem is to
reduce the time horizon for which planning takes place by working in incremental
sprints. Sprints are usually one-four week long periods that begin with the team
planning the sprint ahead of them and often end with a demo of the product,
which will be in a functioning state. There is usually a requirements document -
commonly referred to as a backlog - present, but this is a dynamic document where
requirements can be added, removed, modified and reprioritized between sprints
and a set of requirements are usually picked to be worked on at the start of each
sprint.
When developing a product there must be some way of prioritizing functionality
in that will bring the most business value to the product. There must also be
some way of determining if the implemented functionality reaches quality standards.
Agile teams always employ a customer representative (sometimes referred to as the
product owner) and she fulfills both of these roles. She doesn’t however perform any
actual planning of the work of individual team members as teams themselves are
responsible for deciding how the implementation will be made and how the work
will be divided within the sprint.

2.1.4 Demands of the agile process


In order for a team to be completely agile within a project there arises a set of de-
mands that the team must work towards fulfilling which perhaps aren’t as common
in classical style of waterfall development.
Firstly, personal communication becomes more important. When the cycles are
short and changes occur often it can quickly become cumbersome to continuously
maintain lots of documentation. The regular feedback with the project’s product
owner leads to more time spent on the communication between her and the team.
In software development projects the need for communication often implies that

6
2.2. SOFTWARE TESTING

the code produced by one team member must be comprehensible to other team
members.
Teams are practically required to be small in size and self organizing when there
isn’t a large overall requirements document. As personal communication becomes
more key to success this also limits the team’s size for practical reasons. Require-
ments are worked on one or a few at a time and only the team is assumed to know
best how they should be implemented. The relative independence of the team also
implies that it has to be cross functional where all of the major competences needed
to develop the product are present within the team.
The product has to be in a deliverable state after each development iteration.
Since each sprint typically ends with a demonstration of the product this of course
implies that it can’t be broken at the time of demonstrating and that all of its
functionality must be fully functional; otherwise there would be no way for the
product owner to assure that the requirements have been met.
Testing must be performed often when iterations are short and the product is
assumed to be in a continuously deliverable state. Both newly implemented as well
as old functionality must be frequently tested as the modification of existing code
can easily break what wasn’t broken in the past.

2.1.5 Sibling methods and principles


While there has emerged agile practices that tackles problems of a truly organiza-
tional nature, such as Scrum [10] and Kanban [11], there has also emerged other
methods and principles that can be said to belong to the agile ecosystem but that
are tied more to software specific, technical issues. The main of these arguably
being TDD [3] and Continuous Integration (CI) [4].

2.2 Software testing


An important part of product development is the task of quality assurance, where
it is assured that the product lives up to the expectations of its stakeholders. These
expectations may be divided into groups depending on the nature of the traits they
touch on, such as usability, reliability, performance etc.
In software development the technical quality assurance process is mainly done
in the form of tests. A test is defined as a procedure where a set of actions are
performed on a product where the outcome of the actions are analysed. Actions
may be performed by human hand or by automated systems and the outcomes are
the reactions of the product, its users, stakeholders or environment.

2.2.1 Classes of tests


Software tests may differ in level of detail, type of functionality under test and
prerequisites necessary for their execution. It’s therefore of interest to separate
tests into different classes based on their characteristics.

7
CHAPTER 2. BACKGROUND

• Unit tests operate on a low level and are tightly coupled with source code.
In the best of worlds one unit test always correlates to one minimal piece of
functionality without dependencies to other components, and when this is the
case they are quick to execute.

• Integration tests validate that software components work correctly when com-
bined with each other. Only the interfaces between components are tested
and it’s assumed that each component has been individually unit tested.

• System tests spans the entire system where most or all components have to
be set up and interconnected. This may increase the execution time of the
tests since it demands more computing power and introduces latency between
components - especially when connecting to external services.

• Performance tests measure the performance of critical parts of the system in


terms of execution time, memory usage, network latency etc.

• Usability tests are used to examine how users interact with the product and
how it impacts their overall experience of using the product.

• Acceptance tests determine that the product reaches quality standards set up
by the product’s stakeholders.

2.2.2 Test automation


Manually executing tests is often a time consuming process, regardless of which
class the tests belong to and it’s therefore of interest to consider automating tests
wherever possible. Acceptance and usability tests are generally interactive and
require the involvement of people, which makes them difficult - if not impossible -
to automate. Unit, integration, system and performance tests are on the other hand
of a non-interactive nature, which means that they can be automated and that the
time spent on executing them can be significantly lowered from what it would be if
they would be performed manually.
Long execution time of a product’s test suite forces developers to wait for the
tests to finish, leading to a reduction in productivity as well as an increase in the
likelihood of developers not executing or adding new tests. This may in turn lead to
a reduction in the overall quality of the product, which calls for the need to reduce
the execution time of the tests. One way to accomplish this is to rely heavily on unit
tests rather than system tests. Unit tests are as a rule executed noticably faster
than system tests because of their lack of coupling, but it’s easy for developers
to introduce couplings in unit tests that go unnoticed. Such "unit tests" are in
fact system tests and are as a result slower to execute. Detecting and preventing
coupling in unit tests may thus reduce test execution time and positively impact
the overall quality of a product.

8
2.3. TEST-DRIVEN DEVELOPMENT

2.3 Test-Driven Development


The original purpose of software tests is that they should be used as a means to
validate existing software and it’s thus natural to assume that they should be created
after the actual software that they test has been implemented. With the advent
of TDD [3] materialized the idea that tests can - in addition to verifying existing
software - also be used to shape the software and the process of developing it when
they are created incrementally and iteratively before any actual code is created.

2.3.1 Core idea


The TDD process promotes the idea that each test always tests a minimal piece of
functionality and that they are created one at a time. As tests are written before
production code this leads to an iterative development process with the general
workflow being:
1. Write a concise, minimal test for a new piece of functionality. This test will
fail since the functionality isn’t implemented yet (if it passes one knows that
the functionality either already exists or that the test isn’t designed correctly).

2. Implement the new functionality and run all tests, both the new and pre-
existing ones. Repeat until all tests pass.

3. Clean up the code and ensure that all tests still pass, then return to step 1.
The focus on implementing minimal pieces of functionality one at a time means that
the time it takes to complete each iteration generally ranges between 30 seconds
to a few minutes. This process becomes cumbersome when tests take a long time
to execute and it’s therefore recommended for developers to focus on writing unit
tests instead of system tests.

2.3.2 Benefits
Kent Beck argues in his book Extreme Programming Explained for the following
benefits of practicing TDD [4]:
• Quick feedback. The programmer is instantly aware of whether new function-
alities work as intended and if any already existing functionality has been
broken.

• Task orientation. Writing small tests for each piece of functionality forces the
programmer to decompose problems into small, manageable tasks.

• Focus. Having a single failing test to fix supports the programmer in focusing
on a individual tasks.

• Progress. Unit tests become a reasonable metric for measuring the flow and
progress of the development process.

9
CHAPTER 2. BACKGROUND

• Stability. Letting tests drive the implementation of functionality the program-


mer by design strives for total test coverage.

• Refactoring. Restructuring code becomes a less uncertain task when tests


exist for all previously implemented functionality.

• Maintainability. Further work on the software is made easier when all imple-
mented functionality can be tested.

• Testable software. It’s indeed possible to write untestable code. This kind of
code can be a good place for bugs to remain undetected.

As an addition to this the fact that a software is accompanied by a set of tests


that cover all of its functionality means that developers have a plethora of example
usages of the code and a living form of documentation over how individual pieces
of code are expected to behave.

2.3.3 Effects
Several extensive studies have been made with the intent of measuring the effec-
tiveness of TDD, but the observed effects seem to vary between individual projects
where the observed effects encompass productivity, defect density, maintainability
and code coupling.
Most studies observe positive effects on maintainability, defect density and code
coupling but with varying productivity. Some observe a reduction in productivity
[34], some observe an increase in productivity [16] while some finds no observable
effects [15].

2.3.4 Test-First vs. Test-Last


When discussing TDD it may be of interest to separate the cases when tests are
written before or after the functionality they are intended to test into two categories:
Test-First (TF) and Test-Last (TL). Strict TDD as first described calls for the use
of TF while it’s arguably a common argument amongst individual developers that
one can do TDD using TL by switching the order of steps 1 and 2 in the TDD cycle
and still achieve the same results as when using TF.
Empirical studies conducted on the practice of TDD and the differences between
TF and TL have shown the differences between the two to be inconclusive in terms
of productivity and quality. Some studies show that TF leads to greater code quality
in the form of coupling and greater test coverage [12], [34], while another showed
no noticable difference in quality but a significant increace in productivity when
practicing TF [16].
Productivity and software quality are difficult metrics to measure. Since some
but not all projects seem to have noticable positive increase in these metrics and no
real negative effects have been discovered there exists some degree of justification
for preferring TF over TL.

10
2.4. CONTINUOUS INTEGRATION

2.4 Continuous Integration


When software development becomes a team effort where multiple developers are
required to work with the same code there is the need for synchronizing the additions
and modifications between them. The iterative contribution of individual developers
to a shared code base can be generalized as:
1. Check out code from a main branch.
2. Modify the code in a local working branch.
3. Merge ones work with the main branch.
4. Push the merge and make it the new main branch.
The longer a developer waits with merging the local and main branches in step 3
the more extensive the merging process becomes; this is known as integration hell.
Conflicting changes and broken code is usually discovered at the time of the merging
and the longer developers wait with merging local and main branches the greater
is the amount of conflicts and broken code. There is therefore a motivation for
performing smaller merges often, as opposed to fewer but larger merges.
This practice of continuously integrating working branches between program-
mers within a project is an idea first proposed as a part of Extreme Programming
(XP) [4] and is known as Continuous Integration (CI). The small integration steps
provide earlier warnings of conflicting changes and broken code, while increasing
feedback and greatly reducing the risk of near-release chaos. In the case that a bug
is discovered the choice of reverting the code to an earlier bug-free state instead of
spending time and money debugging becomes a viable option.

2.4.1 Pre-conditions
The frequent merging of different branches practically necessitates the existence
of unit and integration tests. While it’s certainly possible to practice CI without
automated testing this will quickly become an uncertain and tiresome process, as
developers have little or no evidence of whether merges are successful or not if they
don’t manually perform tests after each merge (which takes time and effort).
Manually performing merges several times a day quickly becomes unmanageable
and several developers often have the need to create local branches of the same
source files. There is thus a need for teams to use a central source control system
and tools assisting them in the merging of code. Luckily several of these systems
exist - the most widely adopted today arguably being git [1] and Subversion [2],
both of which allow creating local branches and automatic branch merging.

2.4.2 Automation
A shared, automated build server can be used by teams practicing CI for a small
initial investment in effort with a high payoff in long term automation. The build

11
CHAPTER 2. BACKGROUND

server is responsible for automatically running unit and integration tests, but more
general quality assurance procedures such as measuring and charting performance
over time or extracting and compiling documentation can also be performed auto-
matically. The general idea here is that the effort spent constructing automated
processes will in the end be far outweighed by the benetits that CI brings with it.

2.4.3 Continuous Deployment


Manually deploying software to testing, staging and release environments is often a
time-consuming process. Teams are discouraged from deploying often as the deploy-
ment process can take up to several days [5] and it isn’t unusual that deployments
are done with months apart, regardless of whether the software is deployed to end
users or internally for developent purposes. The need for an automated deployment
process is thus present, but the problem is usually that each deployment requires
manual quality assurance, which is labor intensive.
Having established the quality of the software many times a day using CI opens
up to the possibility of automatically deploying the product to the actual environ-
ment in which it will eventually be released, or at least an environment which is
similar. If the quality assurance process is completely automated deploying the soft-
ware can in the best of worlds be done at the same rate as integration. This method
of short, continuous release cycles is commonly known as Continuous Deployment
(CD).
Shorter release cycles implies that products reach end users at a more rapid
rate and functionality with business value is usable at earlier times which in turn
increases overall profitability. Bug-fixes and patches can also be deployed more
rapidly thus increasing the security of the software while reducing risk.
CD also strives to reduce the risk in the actual deployment of patches and
improvements, since the risk involved in deploying small sets of functionality is
generally much lower than when deploying large functionality changes.

2.4.4 Symbiosis with TDD


The benefits of CI and CD relies heavily on the existence of small unit and integra-
tion tests and great test coverage. Using these practices in conjunction with TDD
solves this dependency automatically and the practice of TDD thus opens up to the
increased benefits of CI and CD.

2.5 Design patterns


Some problems within certain domains of software design can’t be solved at the
language level. Solutions to these problems may come in the form of best practices
and when these solutions are formalized they are known as software design patterns.
Most design patterns in OO contexts are concerned with modularity and coupling
and the way that they are applied varies between individual projects.

12
2.5. DESIGN PATTERNS

2.5.1 Dependency injection


As software is modularized functionality is separated into components. A component
is loosely defined as an entity that serves some kind of distinguishable purpose,
with some examples being classes, functions, external web services etc. When one
component’s functionality relies on the functionality of another component we say
that the former component is the client and the latter the service where the client
depends on the service.
When setting up a dependency from a client to a service one has two choices,
either to let the client itself create a link to the service to let an external coordinating
service pass the service to the client. In the first scenario we have a strong coupling
from the client to the service and it becomes problematic to have a client that can
use serveral services that implement the same API. In the latter scenario we can at
the cost of a little extra coordination overhead create clients that only depend on
the API of a service and not actual implementations of the API.
This method of loose coupling between components where clients only know
of its service’s API and gets instances of the services passed to them by external
coordinating components is known as dependency injection design pattern (the D
in SOLID (SOLID) [14]). Martin Fowler - the person who first coined the term -
identified three ways of implementing the design pattern in OO languages [13]:

• Constructor injection: injecting dependencies into constructors when instan-


tiating objects.

• Setter injection: providing setter methods where objects’ dependencies may


be provided.

• Interface injection: basically the same as setter injection, but the setter meth-
ods are declared in interfaces implemented by client objects.

An example of how a client-service relationship might look without using depen-


dency injection consider the following Java class that is used for logging and that
has a strong coupling to a database:

public class Logger {


private DatabaseExample database;

public Logger() {
this.database = new DatabaseExample();
}

public void log(String message) {


database.write(message);
}
}

13
CHAPTER 2. BACKGROUND

Now assume that we want to create several instances of the logger class where each
instance writes to a different kind of database. This simply isn’t possible in the
above example as each instantiation of the Logger class always creates a connection
to the same database class. Consider the following class that resolves this issue
through constructor injection:
public class Logger {
private DatabaseExample database;

public Logger(DatabaseExample database) {


this.database = database;
}

public void log(String message) {


database.write(message);
}
}
This loosely coupled example shows how one may use dependency injection where
the Logger class is the client that depends on the DatabaseExample service. It’s
now possible to inject any implementation of the service into the client and the
functionality of the client is completely separated from any implementation of the
service.
Implementing the dependency injection design pattern in a system means that
the configuration and creation of links between components can be moved out of
individual components into a single coordinating one, thereby reducing the com-
plexity of the system. It also makes it easy to replace one implementation of a
service used by a client with another one, which in turn enables new ways of test-
ing clients without relying on implementations of services through the use of mock
objects.

2.5.2 Dependency mocking


Although the architectural separation of clients and services through the use of de-
pendency injection easens the process of modularizing software the testing of clients
still require that there exists implementations of the services - since the functional-
ity of clients depends on the functionality of services. But when we introduce both
a client and the services it depends on in the client’s tests we’re implicitly testing
the functionality of the service as well. In other words the tests that are supposed
to be a client’s unit tests are in fact partial or complete system tests, which may in
some cases be problematic:
• Service functionality is slow, making the execution time of unit tests longer.
• Functionality provided by the services are non-deterministic, leading to tests
with unpredictible results.

14
2.5. DESIGN PATTERNS

• State in the services is difficult to set up or tear down.

• The services don’t exist or can’t be used yet.

In the late 90’s a group of developers and system architects began researching ways
of eliminating these kinds of problems in OO systems [20] and the result was a test
design pattern called object mocking [19].
A mocked object is an instance of an object that behaves as an instance of a
certain class but with functionality that is tailored for special use cases without
relying on an actual implementation of the class. They are mainly used in unit
testing of clients, where the values and methods of the services a client is dependent
on are mocked and injected into the client without introducing any actual coupling
between existing components.
Although there has been some controversy regarding the terminology of mocked
objects in term of the functionality they provide [23] there will be no such distinc-
tions made in this thesis and all all sorts of mocked objects will simply be referred
to as mocked objects.
Unit testing components with dependencies is practically impossible without the
mocking of objects. Since TDD relies heavily on unit testing, object mocking is thus
a crucial part of the Test-Driven Development process.

2.5.3 Tests as documentation


As multiple people collaborate in developing software they need some sort of method
communicating about the behavior of the software, such as descriptions of the be-
havior of functions, example code illustrating how the software is used or the overall
system design. This need for communication exists internally between developers
and may also exist externally to the users of the software’s API. The behavior of the
software can of course be communicated verbally between people, but this easily
becomes uncumbersome as developers forget how the software is implemented or
when the amount of required communication increases. It has therefore tradition-
ally been custom to write down the expected behavior of programs into documents
that are shared between those who might need them.
This traditional form of communicating through documents has a weakness in
the sense that the documentation is completely separate from the actual code -
whenever the code is updated developers must also rewrite parts of the documen-
tation, a task that is easily overlooked or forgotten and as a consequence the docu-
mentation may be incomplete and/or contain errors.
One way of facing this problem of inconsistency between code and documenta-
tion is to allow the documentation to be an actual part of the code that doesn’t have
to be maintained separately by letting tests serve as documentation. When each
test tests a single piece of functionality and is constructed in such a way that us hu-
mans are able to understand which functionality is being tested, the test fulfills the
role that the separate documentation was otherwise supposed to serve since we’re

15
CHAPTER 2. BACKGROUND

able to extract information from the test about what functionality is implemented
and examples of how it’s supposed to be used.
The view point of keeping unit tests as documentation has several advantages
when used in conjunction with TDD. Firstly the tests are always up to date and
consistent with the functionality described as they are run continuously during
development when practicing TDD. One therefore never have to worry that the
behavior described is inconsistent with the actual behavior of the software. Similarly
there’s also the positive effect of having no undocumented behavior since TDD
strives for total test coverage of all implemented functionality. Another implicit
effect is the fact that developers won’t have to commit extra time to writing separate
documentation as the tests are already there.
This method does however require that the developers put effort into making
the tests expressive, since other people (and at a later point in time they themselves
too) are supposed to be able to quickly understand which behaviors are being tested.
A common way of achieving this goal is to make each unit test verify one certain
behavior and name it in a descriptive way; an example of how this might look in
Java using JUnit is:

@Test
public void twoPlusThreeEqualsFive() {
assertEquals(5, 2 + 3);
}

Apart from naming the tests most languages support some way of annotating them
with actual text phrases that are supposed to be a description of the tests. By
assembling all of the tests into a list one should this way be able to get a full
description of all behaviors that can be expected from a piece of software and
examining into individual tests provides usage examples.

2.6 Clojure
Clojure is a relatively young programming language with version 1.0 released in
2009 [32]. It is in the words of its creator Rich Hickey [33] a language based around
the core ideas of having a programming language that is a functional Lisp dialect
with an emphasis on immutability and concurrency, while being designed to run
on host platforms with a great deal of transparency to the underlying platform’s
libraries and capabilities.

2.6.1 Syntax
Clojure belongs to the Lisp family of languages - a group of languages that are
all based on a computational model and syntax first described in the late 50’s by
John McCarthy [29]. As with other Lisp dialects all code and data are syntactically
and computationally interchangeable in Clojure, where expressions are written as

16
2.6. CLOJURE

paranthesized lists. The first item in a list is interpreted as a function with the
following items being arguments to the function:

(f arg1 arg2 arg3)

Function calls may be nested to create more complex computations:

(f arg1 (g arg3 arg4) arg2)

Lists, the most important data type in Clojure, are created with function calls:

(list 1 2 3) => (1 2 3)

but in practice lists are often created using syntactic sugar for readibility:

’(1 2 3) => (1 2 3)

Commas and white space are interchangable:

’(1,2,3) => (1 2 3)

2.6.2 Data types


Clojure implements the primitive data types common to most other programming
languages, such as integers, floats, strings etc. There are four collection data types
commonly created with syntactic sugar:

Singly linked lists, as described above:

’(1 2 3)

Directly indexable vectors:

[1 2 3]

Sets that contain unique values:

#{1 2 3}

and key-value maps:

#{:key1 1 :key2 2 :key3 3}

Symbols beginning with a colon are interpreted as atomic keywords. The four main
collection data types share some properties in the sense that they are sequences of
values. They are thus commonly referred to as seqs when their sequence properties
are applicable and seqs can be lazy. Since Clojure is a dynamically typed language
values of different types are thus interchangable in code and their type is checked
at runtime rather than compilation. Collections are allowed to contain values of
different types.

17
CHAPTER 2. BACKGROUND

2.6.3 Concurrency
Clojure is a language with the concept of concurrency built into its core [25]. The
main creator of Clojure, Rich Hickey, has in his work with Clojure reduced problem
of concurrency into the identity-value conflict - the inability to separate references
to values from the values themselves [26]. A reference shared by multiple executing
threads may be altered to refer from one value to another by one of the threads and
this alteration will be visible by the other threads. Similarly the values that are
being referred to are stored in physical memory locations and may also be altered by
other threads during their execution, leading to classical race conditions when mul-
tiple threads reads and writes to the same memory locations simultaneously. When
dealing with concurrency one must thus address the problems of the coordination
of references and the mutability of values. Clojure attempts to solve the former
problem by supplying and forcing the use of several methods of thread-safe resource
management in which references to shared resources are handled and coordinated
in safe ways between threads that doesn’t lead to race conditions and the latter by
implementing a memory model of immutable values where values once never change
after they have been set.

2.6.4 Resource referencing


As with most (if not all) programming languages Clojure has ways of binding sym-
bols to values where a symbol in practice is the textual representation of a reference
to a value which a programmer may use in the source code of an application. Clojure
supplies four ways of doing this with Vars, Refs, Atoms and Agents.
In Clojure symbols are bound to values with Vars - bindings that are either static
(which is the default behaviour) or dynamic and bound on a per-thread basis. If a
Var is static it can’t be rebound and is thus thread safe, while if dynamic the Var
is thread local and thread safe since it can’t be accessed by other threads. The def
special form in the following example binds the symbol my-var to the value "some
text":

(def my-var "some text")

The value referred to by a Var can either be an immutable value, or an immutable


container of a mutable reference to another immutable value that provides thread
safe ways of managing wrapped reference. Clojure provides three ways performing
this mutable reference management with Refs, Atoms and Agents that all implement
a transactional memory model and provide different functionalities:

• Refs provide coordinated synchronous access to multiple resources.

• Atoms provide synchronous access to single resources.

• Agents provide asynchronous access to single resources.

18
2.6. CLOJURE

The common use cases for Atoms are when multiple threads needs access to and the
ability to modify a single resource without the occurrence of race conditions, while
Refs are similarly used for grouping together several resource modifications into a
single atomic transaction. Agents are on the other hand commonly used when the
modification of a resource is asynchronous and the thread accessing the resource
doesn’t have to wait for the modification to execute.

2.6.5 Immutable values


In Clojure a value may never change (mutate) once it has been created and stored
in memory, which means that Clojure’s values are immutable. The only way of
changing the value that a reference points to is thus to make the reference to point
to a new value stored in a different memory location. This differs from how memory
is clasically treated in OO languages, where the reference may continue to point at
the same memory location while the actual value stored at that memory location
changes.
The Clojure way of maintaining the immutable state of values ensures that one
can always be sure that the value read by a thread will always stay the same,
regardless of mutations performed by other threads. In practice this also means
that one doesn’t have to worry about a value changing after it has been passed to
a function and the process of creating thread safe, concurrent programs is made
simpler.
Creating new values such as eg. integers is cheap, but when dealing with large
data structures - such as lists containing thousands of items - making a new copy
of the data each time the list is modified would of course be an expensive oper-
ation. Clojure thus implements collections as self referencing trees where making
new copies is cheap both in memory consumption and processing time [27]. Though
this is a rather complex structure when compared to eg. singly linked lists, the im-
plementation is hidden away behind layers of abstractions and programmers may
use the collection data structures without any knowledge of their actual implemen-
tation.

2.6.6 Platform
Clojure is hosted on the JVM platform and shares its garbage collection, threads
etc. Clojure source code is interpreted and compiled to JVM bytecode either ahead
of time or at runtime and Clojure supports the ability to natively implement Java
interfaces and classes. It’s also possible in Clojure to interact with existing Java
code and developers thus have easy access to already existing Java code.
There has been some attempts made to port the Clojure language to other
platforms as well, some notable examples being ClojureScript [30] that compiles
Clojure code to JavaScript and ClojureCLR [31], a Clojure implementation on the
.NET platform. These spin offs aren’t however within the scope of this thesis and
- although interesting in their own right - won’t be discussed in detail.

19
CHAPTER 2. BACKGROUND

2.6.7 Special forms


Most of the core functions in Clojure are written in actual Clojure code, but in order
for the high level Clojure code to be able to communicate with the machine there
must exist some kind of link between Clojure code and the underlying platform.
These links come in the form of functions known as special forms and they are
implemented in Java code. Among them are eg. the if-then-else special form and
the def special form. Although they are treated differently by the Clojure compiler
than other Clojure code, there are no practical differences in how they are used by
the programmer.

2.6.8 Macros
A macro is a tool for altering a data structure into code which can then be in-
terpreted by the Clojure compiler. Macros can be defined and expanded by pro-
grammers, thus allowing the core language to be extended with new syntactical
constructs. This is made possible by the fact that Clojure is homoiconic, ie. the
structure of its source code is the same as the structure of its abstract syntax tree.
An example of a Clojure macro is the "thread-first" macro:

->

which in the fictional example:

(-> "some text" read parse evaluate print)

is evaluated as:

(print (evaluate (parse (read "abc"))))

after it has been expanded by the Clojure compiler.

20
Chapter 3

Method

The methods used in this thesis consists of a study and analysis of literature, as
well as programming activities. The work began with an extensive literature study
along with an analysis of how OO practices could be applied in Clojure, specifically
taking into consideration of the differences between Java and Clojure. To get a first
hand understanding of how the TDD process differs between development in Java
versus Clojure and what consequences these differences may lead to there were two
identical programs created in both languages.

3.1 Litterature study


Clojure is a relatively young language with an active community of bloggers and
enthusiasts. As a result of this the available literature on development practices and
experiences in Clojure are mostly anectodal rather than academical. The problem
of trying to analyze this litterature thus consists of trying to sift out general and
objective facts.
Lisp dialects have existed for many decades and some research has been pub-
lished on the lingual benefits of using Lisp syntax and code structure. Since Clojure
is a dialect of Lisp this research has been taken into consideration when applicable
in Clojure.
There exists an abundance of research done in the field of TDD but these have
largely been written in OO contexts. These served as a platform and reference
point for justificating conclusions on how the TDD process may be affected by the
differences Java and Clojure.

3.2 Programming activity


In order to get an understanding of how the creation of tests, their impact on
software design patterns, the available tooling and the overall TDD process differs
between the languages there were two applications created from scratch - one in

21
CHAPTER 3. METHOD

Clojure and one in Java - that have identical external interfaces and thus can be
considered to provide the same functionality.
For the programming activity to be relevant it was important that the applica-
tions touched on the more complex parts of unit testing and the criteria put on the
applications were that they should:

• Have a minimal reliance on external language-dependent libraries

• Implement a high level of modularity

• Introduce several mockable services

• Maximize the amount of unit-testable functionality

There were however no demands made on the performance of the applications, other
than that they were reasonably similar and effective.
The application that was chosen to be developed in both languages was an
implementation of the game Checkers [18] that has a text only interface and is run
by automated AI’s. It’s assumed that the time it took to develop two versions of
the game was within the scope of the time available for this thesis.
These implementations were chosen for several reasons. First of all the minimal
text based UI and the fact that AI’s play the game leads to little user interaction.
Testing user interaction is thus limited and more focus can be put into testing game
functionality with unit tests. Secondly there are several variants of Checkers with
different board sizes and rules, which opens up for the possibility of modularity,
inversion of control and dependency mocking. Lastly there’s no need for external
language-dependent libraries, which means that the implementations can be com-
pletely written in native code.

22
Chapter 4

Results

4.1 How Clojure differs


The first step in determining how the differences between Java and Clojure affects
unit testing and the TDD process is to examine in which fundamental ways the two
languages differ. This thesis identifies the following core features found in Clojure
and not in Java:

• Dynamic execution

• Dynamic typing

• Lisp syntax

• Macros

• Namespaces

• Immutable values

• Transactional memory

• First-class functions

• No object-orientation

• Creating documentation

• Available tooling - such as editors, frameworks, and debuggers

By closely examining this list of features it’s possible to observe and deduce how
they may affect unit testing and the TDD process.

23
CHAPTER 4. RESULTS

Observation 1 - Macros
The arguably most striking difference between the two languages is the fact that
macros are a core feature of Clojure but not Java. There’s no practical way of
creating self modifying code in Java and as a consequence of this there’s also no
experience in how such code should be tested. Section 4.2 will explain and motivate
one way of testing macros in Clojure.

Observation 2 - Dependency injection


The idea of dependency injection is key in TDD since the injecting of dependencies
is a pre-requisite in the process of dependency mocking, which in turn is practically
a necessity if one wants to be able to rely on unit testing of coupled components.
Dependency injection has however primarily been described in an OO context where
dependencies are injected into classes. Due to Clojure’s lack of OO one thus have to
find other ways of injecting dependencies that don’t rely on the existence of classes.
Section 4.3 will explain and motivate several ways of injecting dependencies into
components in Clojure.

Observation 3 - Dependency mocking


A dependency in Java generally comes in the form of a class and mocked objects are
thus ad hoc instances of classes. Since Clojure is classless the dependencies have to
take on other forms of data structures than classes and section 4.5 will explain this
further. It will also show how one is able to inline mock objects directly in tests
without the need for any frameworks, due to the dynamic nature of Clojure.

Observation 4 - Documentation
Documentation is an important part of all software projects. In Java the JavaDoc
tool is industry standard for coupling documentation and code together but Clojure
doesn’t have a similar tool. One must thus find another way of documenting code.
The principle of keeping unit tests as documentation might also be different in
Clojure when compared to Java due to the differences in syntax and test tooling.
Section 4.6 will explain ways to tackle these two problems in Clojure.

Observation 5 - Loading namespaces


The way namespaces are loaded in Clojure means that any runnable code that isn’t
a definition is run when the namespace is loaded, thus creating an opportunity for
undestable code to creep in. Section 4.4 gives a short explanation of how this works
and how it should be avoided so that code can be clean and unit testable.

24
4.2. TESTING MACROS

4.2 Testing macros


Although the prospect of testing self modifying code might seem daunting at first,
it’s actually a trivial task to do so in Clojure. Remember that Clojure code is
structured as lists and that a macro is basically a function that restructures a piece
of code. Testing a macro is thus the simple task of validating that the macro
correctly expands input lists into output lists.
As an example consider a macro that takes an expression written in infix notation
(which of course Clojure can’t interpret by default) and translates it into a regular
Clojure prefix notation expression:

(defmacro infix [expr]


(concat (list (second expr) (first expr))
(drop 2 expr)))

This macro is expected to make the transformation:

(infix (a f b)) => (f a b)

To test that the macro works as intended we simply expand the macro on a sample
expression and compare it to the expected result:

(= ’(+ 1 3) (macroexpand ’(infix (1 + 3))))

It should be obvious that testing macros is just like testing regular list transfor-
mations, which shouldn’t be an unfamiliar task to reasonably experienced Java
programmers. The above above example is of course rather simple and macros may
be structured in more complex ways, but the principle of list transformation still
stays the same.

4.3 Dependency injection in Clojure


The basic component in Java and other OO languages is the object, which by design
is always an instance of a class (one could argue that the primitive data types are
also basic components in Java, but because these types are so simple they won’t
be considered as components for these purposes). Since dependency injection is the
principle of linking together components and Clojure doesn’t implement any form
of classes the basic component in Clojure must be defined as something other than
a class if one wants to practice dependency injection in Clojure.
The basic component in Clojure will instead be defined as either a function, a
data structure or a value. Values and data structures are objects that only contain
values without performing any computations and it does therefore not make sense
to pass other components into them as dependencies. The process of injecting
dependencies in Clojure is thus the process of dynamically providing functions access
to other functions, values and/or data structures.

25
CHAPTER 4. RESULTS

Bringing a set of dependencies into the context in which a function executes


can be done in two ways - either by passing the dependencies as arguments to the
function or by binding the dependencies in the context in which a function is called.
Let’s begin by taking a look at how dependencies might be passed as arguments
to functions. This can be done in two ways, by passing each dependency as a single
argument or passing sets of dependencies as single arguments.

4.3.1 Each dependency as an argument


This is arguably the most trivial design for injecting dependencies. Consider the
implementation of two service functions used for saving and fetching data from some
kind of storage:

(defn save [coll id]


(... implementation of function ...))

(defn fetch [coll id timeout]


(... implementation of function ...))

A client function that depends on these two operations might look something like:

(defn my-fun [save fetch coll id timeout]


(... call (save coll id) ...)
(... call (fetch coll id timeout) ...))

The service functions might with this simple design be mocked when testing or
replaced with other implementations with the same API. Calling the client function
with implementations of its dependencies is trivial:

(my-fun save fetch coll id timeout)

A problem with this design is that as the number of dependencies increase for the
client function it may become an uncumbersome way to redirect dependencies. Code
becomes more cluttered and thus less readable, as well as making refactoring more
troublesome.

4.3.2 Set of dependencies as a map


With this design all dependencies for a function are contained within a single data
structure, with the intention of leaving the code less cluttered, and there are a
few practical ways of doing this. The arguably easiest way of bundling together
objects and functions into a single dependency object is by using maps. Objects
and functions can be trivially inserted into and retrieved from a map using keys and
there is of course no need to use every value in the dependency map, meaning that
the same dependency map can be passed to many different functions without any
extra overhead. Using the above example the client function would be redefined as:

26
4.3. DEPENDENCY INJECTION IN CLOJURE

(defn my-fun [storage coll id timeout]


(... call ((:save storage) coll id)
(... call ((:fetch storage) coll id timeout))

The already defined service functions are put in a map:

(def storage {:save save


:fetch fetch})

And the client function would be called with the map as an argument:

(my-fun storage coll id timeout)

4.3.3 Set of dependencies as a protocol


When there exists multiple unique and interchangeable implementations of a depen-
dency with the same externally visible interface it’s possible for the sake of clarity
and extensibility to use protocols. A Clojure protocol is a declaration of a set of
function signatures that might be implemented in multiple ways, which makes pro-
tocols similar to Java’s interfaces. Protocols may be defined with the defprotocol
special form:

(defprotocol Storage
(save [this coll id])
(fetch [this coll id timeout]))

And the protocol may have multiple service implementations that are created with
the reify macro:

(def db-storage
(reify Storage
(save [this coll id]
( ... implementation of function ...))
(fetch [this coll id timeout]
( ... implementation of function ...))))

(def in-memory-storage
(reify Storage
(save [this coll id]
( ... implementation of function ...))
(fetch [this coll id timeout]
( ... implementation of function ...))))

These service implementations may later be passed to the client function regardless
of which implementation is used:

27
CHAPTER 4. RESULTS

(defn my-fun [storage coll id timeout]


(... call (save storage coll id) ...)
(... call (fetch storage coll id timeout) ...))

4.3.4 Dependencies as context


The main idea behind this design is that a client function gains access to its depen-
dencies through the external scope in which it’s executed, rather than as arguments.
Both methods will in practice produce the same results, as arguments can be seen
as belonging to the the execution scope of a function, but the resulting code differs.
The above examples would with this method looks like:

(defn my-fun [coll id timeout]


(... call (save coll id))
(... call (fetch coll id timeout))

The service functions must then be bound in the context which calls the function:

(with-redefs [save (... set up dependency ...)


fetch (... set up dependency ...)]
(my-fun coll id timeout))

Though it’s entirely possible to use this approach effectively it has two important
implications. Firstly the compiler will complain upon loading the client function
that the service functions aren’t defined within the client function’s context. The
service functions must thus be bound to some default value for the code to be able
to compile. Secondly when dependencies are purely contextual two calls to the
same function and with the exact same arguments may produce differing results
depending on where they are placed in the code, as opposed to calls to functions
whose dependencies are all arguments.
The fact that the function creates different results depending on the context in
which it’s executed means that it’s impure, while the previous examples of client
functions that aren’t context dependent are pure (assuming that the service func-
tions are pure as well). Pure functions are generally easier for programmers to wrap
their heads around and reason about since one doesn’t have to expand one’s focus
outside of the internal context of a function. It’s therefore recommended to opt
for the approach of passing dependencies as function arguments rather than having
dependencies as bound by context.

4.4 Untestable code in namespaces


Code placed in OO constructors that performs logic is a breeding ground for untestable
code. When a dependency is set up within an objects constructor rather than be-
ing injected as a dependency it’s practically impossible to mock or replace that
dependency since that code is run at the creation of each instance of the class.

28
4.5. MOCKING IN CLOJURE

In Clojure there are no classes and therefore no constructors either, but a similar
problem still exist with namespaces. By design Clojure evaluates all expressions
present within a namespace as it is loaded at runtime. This means that any loose
logic within a namespace that is something other than a pure definition will be run
as the namespace is loaded. As an example of this consider the following namespace:

(ns example-namespace)

(def example-var ...)

(print "Hello")

(defn example-function ...)

Loading this namespace will apart from bringing the var and function into scope
also print the string "Hello", i.e. the loading of the namespace is not free from side
effects. Although this example has a rather harmless side effect it’s possible that
there may be code run that leads to more complex behaviors. These behaviors are
in the best of cases hard to test and in the worst untestable, where dependencies are
impossible to mock. For the sake of testability one should thus avoid including any
logic in namespaces that will be evaluated when the namespace is loaded if possible.

4.5 Mocking in Clojure


Mocking anything but the most simple objects in Java practically necessitates the
use of external frameworks that support the creation and inspection of mock objects.
While external mocking frameworks are available in Clojure, such as Midje [24], it’s
both technically and practically possible to create mocked functions and objects in
native Clojure without the use of external frameworks. How this may be done will
be shown in a few general scenarios.

4.5.1 Mocking injected functions


Consider the following example client function add-and-square, whose functional-
ity depends on the service function add-fn injected as a function argument:

(defn add-and-square [add-fn x y]


(let [z (add-fn x y)]
(* z z)))

If one wants to test this client function without depending on an actual implemen-
tation of the service function add-fn one will have to set up a mock function that
can be used as an argument in a unit test:

(deftest test-add-and-square

29
CHAPTER 4. RESULTS

(let [add-fn (fn [x y] 5)]


(is (= 25 (add-and-square add-fn 2 3)))))

In this example the mocked function add-fn always returns 5, regardless of which
arguments it’s called with. Instead consider that one wants to make a mocked
function that returns different values depending on which arguments it’s called
with. One arguably simple way of doing this is by pattern matching and mapping
the expected input arguments to custom output values:

(deftest test-add-and-square
(let [add-fn (fn [x y]
(case [x y]
[2 3] 5
[3 5] 8))]
(is (= 25 (add-and-square add-fn 2 3)))
(is (= 64 (add-and-square add-fn 3 5)))))

4.5.2 Mocking non-injected functions


Imagine that one didn’t - for some reason - opt to go the dependency injection
route in the first example above and instead introduced a hard coupling from the
add-and-square function to the add-fn function:

(defn add-and-square [x y]
(let [z (add-fn x y)]
(* z z)))

If we want to create a true unit test for this client function we must be able to do
this without depending on an actual implementation of add-fn. This is possible by
rebinding the symbol bound to add-fn in the context in which add-and-square is
executed:

(deftest test-add-and-square
(with-redefs [add-fn (fn [x y] 5)]
(is (= 25 (add-and-square 2 3)))))

4.5.3 Verifying function calls


It’s not unusual when testing client functions that it’s only important to know if a
service function has been called or not, without caring about which values the service
function returns. As an example consider the client function parse-if-active that
takes an event and a service function parse-fn as input arguments and makes a
call to the service function if the status of the event is active, without caring about
any return values:

(defn parse-if-active [parse-fn event]

30
4.6. DOCUMENTING CLOJURE

(if (= :active (:status event))


(parse-fn event)))

Verifying that a function has been called a number of times in Clojure - with the
ability to check with which arguments the function was called - is a bit trickier than
just mocking function behavior without the use of frameworks, but its’ possible to
do this in a few rows of native Clojure code. One way of doing this is:

(deftest test-parse-if-active
(let [event {:status :active, :value :some-value}
args (atom [])
parse-fn (fn [event] (swap! args conj event))]
(parse-if-active parse-fn event)
(= @args [event])))

This approach creates a mutable vector args that will contain the arguments that
the mocked service function parse-fn will be called with. parse-fn is implemented
in such a way that each time that it’s called it will append its input arguments to the
args vector. Checking if the parse-fn function was called with certain arguments
is then just a matter of inspecting the args vector. Counting the number of times
the parse-fn function was called is equal to counting the number of elements in
args and since the each insertion into the vector is done at its tail it’s possible
to check in which order multiple calls to the function was made by doing index
comparisons.

4.6 Documenting Clojure


The industry standard for documenting Java source code is arguably through JavaDoc
[28], a tool bundled as a part of the Java language. With JavaDoc developers man-
ually maintain the documentation which becomes a part of the source code. This
documentation is however independent of the software in the sense that it’s pure
text that can be extracted and parsed into HTML out of the source files, but
isn’t present in the compiled software at runtime. An example of how a section of
JavaDoc documentation might look in source code is:

/**
* The mathematical operation of exponentiation.
*
* @param b The base of the operation.
* @param n The exponent of the operation.
* @return b to the power of n (b^n).
*/
public int exp(int b, int n) {
... implementation of method ...
}

31
CHAPTER 4. RESULTS

Clojure supports a different approach to documenting code where documentational


text is stored as metadata for Vars and special forms. When the documentation
is stored as metadata it isn’t only present in the source files but is also available
at runtime, which can be useful when interacting with applications through the
Read-Eval-Print Loop (REPL). An example of how documentation is created as
metadata in Clojure is:

(defn exp
"The mathematical operation of exponentiation.
Returns the value of b to the power of n (b^n) where
b is the base and n is the exponent of the operation."
([b n]
(... implementation of function ...)))

Both methods requires that developers maintain the documentation manually and
there’s thus little difference between them in practice.

4.6.1 Expressiveness of tests in Clojure


For tests to be expressive enough to be able to be considered as an alternative way
of documenting software the testing environment must satisfy a few conditions. It
must within the testing environment be possible for developers to:

• create tests that test one piece of functionality,

• follow the body of a test and understand what is being tested, and

• give meaningful and representative descriptions of each individual test.

As will be shown it’s in fact true that Clojure’s native testing environment satisfies
all of these conditions.
In order to test an individual piece of functionality within a single test one must
to be able to separate the functionality under test from its dependencies. As we have
already shown it’s entirely possible to mock dependencies in Clojure and assuming
that the functionality under test has been implemented in a modular fashion it’s
therefore possible to create unit tests that test an individual piece of functionality.
It shouldn’t be a problem for a developer to look at the body of a test and
understand what is being tested as long as the test is cleanly written and the
developer has a decent understanding of the language.
Giving a meaningful description of a test can in practice be done in two ways
- either by linking the test with a descriptive text string or by giving the test a
name that is descriptive of the functionality that is being tested. Since it’s possible
to name functions in clojure using regular letters and many of the other ASCII-
characters the naming of functions can be done in an arguably descriptive way:

(deftest two-plus-three-is-five
(is (= 5 (+ 2 3))))

32
4.6. DOCUMENTING CLOJURE

If one for some reason would rather describe a test in the form of free text this is
supported by Clojure’s native testing framework by associating the assertion with
a string:

(deftest a-test
(is (= 5 (+ 2 3))) "two plus three should be equal to five")

or by wrapping multiple tests in several descriptions:

(deftest several-tests
(testing "mathematical operations"
(testing "addition"
(is (= 5 (+ 2 3)))
(is (= 8 (+ 6 2))))
(testing "multiplication"
(is (= 8 (* 4 2)))
(is (= 0 (* 0 7))))))

Regardless of which way one chooses to describe one’s tests it should be obvious
that it’s indeed possible to create just as expressive and descriptive tests in Clojure
as one may in Java.

33
Chapter 5

Discussion

This thesis is based around the hypothesis that the differences between Clojure and
Java would in turn lead to great differences in how unit testing is done and TDD is
practiced in both languages respectively. As this hypothesis was analyzed closer it
became more and more apparent that the results expected to be found weren’t as
dramatic as first assumed.
This is likely due to the fact that even though the languages differ much in
appearance and implementation they still operate within the same domain. Being
general purpose languages means that everything that is possible to do in Clojure
is also possible in Java. Although the manner in which developers may choose
to create their implementations and thus their unit tests may differ, the fact still
remains that unit tests are simply small, modular tests that test certain pieces of
functionality. As long as it’s possible to separate dependencies it’s also possible to
create idiomatic unit tests. Thus the concern of trying to find the differences in how
unit testing is affected by the lingual differences is mainly the problem of trying to
create modular code without couplings.
One greatly impactful difference between the two languages unrelated to depen-
dency separation is the tools available to developers that may be used for develop-
ment in each language. Both languages have active communities that continuously
find new problems and invent tools that attempts to solve them and one can’t for-
get that each individual developer has the freedom to choose her own development
environment. This makes it difficult to make any sort of analysis on tooling differ-
ences and any attempt to do so would merely be a snapshot of the current situation
that would quickly become outdated. It’s however safe to say that the fact that
Clojure has been around for a significantly shorter time than Java means that there
are naturally fewer and less mature tools for Clojure developers to choose from.
Perhaps this will change in the future, though.

35
CHAPTER 5. DISCUSSION

5.1 Lack of IDEs


There exists a few all encompassing Integrated Development Environments (IDEs)
for Java development, like Eclipse [35] and IntelliJ [36], in which developers basically
have everything they need, from package management and automatic builds to code
completion and refactoring tools. Clojure does currently not enjoy this kind of
luxury with end-all be-all IDEs and one must thus instead use an ecosystem of
specialized tools.
While there exists Clojure plugins to major IDEs like Eclipse and IntelliJ these
are lacking in certain features and still relies on external Clojure specific tools, the
primary one being Leiningen [38] for compilation, running tests and executing ap-
plications. Other alternatives for Clojure development is using highly customizable
text editors like Emacs [37] and importing settings that easens their use, but this
is arguably an environment with a steep learning curve that isn’t very user friendly
for beginners. Another promising tool that aims to be an all encompassing, while
still user friendly, IDE is LightTable [39]. This tool is aimed primarily at Clojure
development, but is today still in its beta stage and still in development.
The fact that there are no stable, all encompassing IDEs for Clojure development
means that the developer gets less support in her development process than she
would when developing Java projects. This becomes especially striking when one
considers code refactoring and modularity. It’s not unusual that a developer realises
after a while of coding that the code contains strong couplings. Separating the
couplings and thus creating more modular code would then require that the code
is refactored into separate parts with couplings between them. Most Java IDEs
support developers in this refactoring but the same support is lacking in Clojure
development tools. Modularizing one’s already existing code is thus harder to do in
Clojure than in Java, which would imply that the highly modularized pattern that
the TDD process requires in order to be practiced effectively is harder to achieve
in Clojure than in Java, when regarding the refactoring of code. One doesn’t get
much support in creating and automatically running tests when lacking IDE support
either, which would clearly have a negative impact on the TDD process.

5.2 Problems of debugging


Debugging is an essential part of the development process and it’s something that
one would rather spend as little time on as possible. The ability to pause a process
during its execution and inspect its state is arguably one of the most fundamental
parts of debugging. This is why most modern IDEs support the use of breakpoints
- flags inserted into code that temporarily haults the execution of a program and
allows developers to do just that.
Clojure development environments are lacking in the support of breakpoints.
In the complementary source code to The Joy of Clojure [40] the author presents
a method of manually creating breakpoints through the use of a relatively simple

36
5.2. PROBLEMS OF DEBUGGING

macro. This does however require that breakpoints are inserted directly into the
source code rather than as flags in the editor. It also requires that the program
is executed interactively through the REPL, which isn’t suitable for all circum-
stances. Although a usable method in small projects it’s doubtful that it’s practical
to implement in larger projects.
The arguably most complete external tool suite for debugging Clojure code is
Ritz [41], an Emacs plugin that supports management and inspection of a programs
state during execution. Using an Emacs plugin isn’t however a suitable choice for
many developers and the user friendliness of Ritz’s text-based interface is debatable.
As a program crashes during execution it’s useful to be able to extract infor-
mation about what caused the crash. Clojure, like many other languages, provide
this information through the presentation of a stack trace along with a simple error
message. Consider the following function that (wrongly) tries to apply a number as
a function:

(defn -main []
(dorun (map 5 ["a" "b" "c"])))

This will cause an error during execution and the developer is provided with a
stacktrace:

Exception in thread "main" java.lang.ClassCastException:


java.lang.Long cannot be cast to clojure.lang.IFn
at clojure.core$map$fn__4207.invoke(core.clj:2485)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:60)
at clojure.lang.RT.seq(RT.java:484)
at clojure.core$seq.invoke(core.clj:133)
at clojure.core$dorun.invoke(core.clj:2780)
at st_example.core$_main.invoke(core.clj:3)
at clojure.lang.Var.invoke(Var.java:411)
at user$eval5$fn__7.invoke(form-init529821451174665787.clj:1)
at user$eval5.invoke(form-init529821451174665787.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6619)
at clojure.lang.Compiler.eval(Compiler.java:6609)
at clojure.lang.Compiler.load(Compiler.java:7064)
at clojure.lang.Compiler.loadFile(Compiler.java:7020)
at clojure.main$load_script.invoke(main.clj:294)
at clojure.main$init_opt.invoke(main.clj:299)
at clojure.main$initialize.invoke(main.clj:327)
at clojure.main$null_opt.invoke(main.clj:362)
at clojure.main$main.doInvoke(main.clj:440)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:419)
at clojure.lang.AFn.applyToHelper(AFn.java:163)

37
CHAPTER 5. DISCUSSION

at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)

As can be clearly seen this relatively simple error provides a long, cryptic stack
trace that reveals much of the internal workings of Clojure and which doesn’t help
the developer much in tracing the source of the crash, especially when logic is nested
through several layers of function calls.
Overall one could argue that debugging Clojure code is much harder than de-
bugging Java code, mainly due to the fact that there exists little IDE support for
inspecting executing programs and error tracing. This is sure to affect the develop-
ment process negatively, regardless of whether TDD is practiced or not.

5.3 Dynamic typing and productivity


When modifying existing code from using one type of value to another one must in
statically typed languages like Java change the type signature for all usages of the
value, while none of that is necessary in dynamically typed languages like Clojure.
The extra time it takes to change type signatures is insignificant, but it’s possible
that the extra effort required for developers to make these changes is substantial
enough for there to be a negative effect on their will to alter existing code.
If this is the case then it will have an especially negative impact on the TDD
process because of the method of continuously making small but many modifications
of existing code that TDD promotes. The fact that Clojure is built on a core of
a very few basic types of data structures lessens the chance of type collissions and
it’s thus argued that one might see an increase in productivity when developing in
Clojure as compared to Java in regards of choosing types for values in the source
code.

5.4 Mocking made easier


The introduction of mocking objects in Java came as an afterthought long after
the language had been created and established in the industry and had to be done
so through external frameworks and not as part of the core language. Though
it’s possible to create mocked objects without the use of frameworks in Java, this
becomes so verbose that it’s arguably not practical.
The fact that the basic mockable component in Clojure is a function, rather than
a class as in Java, means that mocked functionality is generally smaller. This in turn
leads to the declaration of mocks being less verbose and easier to comprehend due
to their smaller scope. Couple this with the fact that idiomatic Clojure functions
tend to be small, composable and preferably pure and we get an overall test design
where mocks are smaller and simpler, which would have a positive effect on the
creation and maintenance of unit tests.

38
5.5. THE REPL AND AUTOMATED TESTS

5.5 The REPL and automated tests


The interactive REPL is a useful tool for exploring core functions and libraries in
Clojure and it’s also of use for developers when testing the behavior of their software
as it’s being developed.
Manually testing software through the REPL has a drawback however in the
sense that none of the code executed there will be stored as actual unit tests,
which means that developers might easily forget to add test cases to the software’s
automated test suite. This will in turn lead to incomplete test suites and we know
that this is bad for several reasons, especially when practicing TDD. The REPL
might thus lead to increased productivity as it’s easier for developers to interact with
their software, but it may also lead to incomplete test suites and thus negatively
impact the TDD process when developers forget to add and automate tests.

39
Chapter 6

Conclusions

Although Java and Clojure are two fundamentally different languages this does not
have a big impact on how unit tests are created. It was observed that the basic
component with regards to code modularization is the class in Java and the function
in Clojure. This leads to different implementations of the dependency injection
pattern where dependencies are injected directly into function calls in Clojure. The
dynamic typing of Clojure allows for mocked dependencies to be easily created
inlined in code without the need for external frameworks.
Logic placed in constructors of Java classes is difficult to test at best and
untestable at worst. Clojure faces a similar problem stemming from a design fea-
ture that allows code within a namespace to be executed when the namespace is
loaded. Such code should be avoided whenever possible. Similar parallels can be
drawn between the testing of macros, where testing a macro is the same as testing
a list transformation - a task that should feel comfortable for any experienced Java
developer.
There is currently a noticable deficit in the development tooling ecosystem of
Clojure as compared to Java. The interactive REPL may possibly lead to increased
productivity but there is a lack of extensive debugging tools and user friendly edi-
tors which might negatively impact the TDD process by slowing down the flow of
development. Clojure has however not been around for long as an established pro-
gramming language, so it’s possible that this may come to change in the foreseeable
future.

41
Bibliography

[1] http://git-scm.com, Retrieved on 2014-09-11.


[2] Apache Software Foundation, http://subversion.apache.org, Retrieved on
2014-09-11.
[3] K. Beck, Test Driven Development: By Example, 1st ed. Addison-Wesley, 2002.
[4] K. Beck, Extreme Programming Explained: Embrace Change. Addison-Wesley,
1999.
[5] J. Humble, C. Read and D. North, "The Deployment Production Line," Proceed-
ings of AGILE 2006 Conference, 2006.
[6] TIOBE Software BV, "TIOBE Index for August 2014," http://www.tiobe.com/
index.php/content/paperinfo/tpci/index.html, Retrieved on 2014-09-11.
[7] W. Royce, "Managing the Development of Large Software Systems," Proceedings,
IEEE WESCON, pp. 1-9, Aug. 1970.
[8] D. Parnas and P. Clements, "A Rational Design Process: How and Why to
Fake It," IEEE Transactions on Software Engineering, Volume 12 Issue 2, pp.
251-257, Feb. 1986.
[9] K. Beck et al., "Manifesto for Agile Software Development," http://
agilemanifesto.org, Retrieved on 2014-09-11.
[10] J. Sutherland, "Agile Development: Lessons Learned From the First Scrum,"
Cutter Agile Project Management Advisory Service. Executive Update, Volume
5 Number 20, Oct. 2004.
[11] D. Anderson. Agile Management for Software Engineering: Applying the The-
ory of Constraints for Business Results. Prentice Hall, 2003.
[12] L. Madeyski. Test-Driven Development - An Empirical Evaluation of Agile
Practice. Springer, 2010.
[13] M. Fowler, "Inversion of Control Containers and the Dependency Injection pat-
tern," http://www.martinfowler.com/articles/injection.html, Retrieved
on 2014-09-11.

43
BIBLIOGRAPHY

[14] R. Martin, "The Principles of OOD", http://butunclebob.com/ArticleS.


UncleBob.PrinciplesOfOod, Retrieved on 2014-09-11.

[15] N. Nagappan, E. Maximilien, T. Bhat and L. Williams, "Realizing Quality


Improvement Through Test-Driven Development: Results and Experiences of
Four Industrial Teams, " Empirical Software Engineering, Volume 13 Issue 3,
pp.289-302, 2008.

[16] H. Erdogmus, "On the Effectiveness of Test-first Approach to Programming,"


Proceedings of the IEEE Transactions on Software Engineering, Volume 31 Issue
1, pp. 226-237, 2005.

[17] A. Miller, "A Dependency Injection Pattern in Clojure," http://tech.


puredanger.com/2014/01/03/clojure-dependency-injection/, Retrieved
on 2014-09-11.

[18] "International Draughts," http://en.wikipedia.org/wiki/International_


draughts, Retrieved on 2014-09-11.

[19] T. Mackinnon, S. Freeman and P. Craig, "Endo-Testing: Unit Testing with


Mock Objects, " Extreme Programming Explained, pp. 287-301, Addison-Wesley,
2001.

[20] S. Freeman. Growing Object-Oriented Software, Guided By Tests. Addison-


Wesley, 2009.

[21] Clarius, Manas and InSTEDD, https://github.com/Moq/moq4, Retrieved on


2014-09-11.

[22] S. Faber, https://code.google.com/p/mockito/, Retrieved on 2014-09-11.

[23] M. Fowler, "Mocks Aren’t Stubs, " http://martinfowler.com/articles/


mocksArentStubs.html, Retrieved on 2014-09-11.

[24] https://github.com/marick/Midje, Retrieved on 2014-09-11.

[25] R. Hickey, "Rationale," http://clojure.org/rationale, Retrieved on 2014-


09-11.

[26] R. Hickey, "State," http://clojure.org/state, Retrieved on 2014-09-11.

[27] R. Hickey, "Persistent Data Structures and Managed References," http://www.


infoq.com/presentations/Value-Identity-State-Rich-Hickey, Retrieved
on 2014-09-11.

[28] Oracle, http://www.oracle.com/technetwork/java/javase/


documentation/index-jsp-135444.html, Retrieved on 2014-09-11.

44
BIBLIOGRAPHY

[29] J. McCarthy, "Recursive functions of symbolic expressions and their computa-


tion by machine, Part I," Communications of the ACM, Volume 3 Issue 4, pp.
184-195, Apr. 1960.

[30] http://clojurescript.net, Retrieved on 2014-09-11.

[31] https://github.com/clojure/clojure-clr, Retrieved on 2014-09-11.

[32] R. Hickey, "Clojure 1.0", http://clojure.blogspot.se/2009/05/


clojure-10.html, Retrieved on 2014-09-11.

[33] "Expert to Expert: Rich Hickey and Brian Beckman - In-


side Clojure," http://channel9.msdn.com/shows/Going+Deep/
Expert-to-Expert-Rich-Hickey-and-Brian-Beckman-Inside-Clojure/,
Oct. 2009, Retrieved on 2014-09-11.

[34] T. Dogsa, D. Batic "The effectiveness of test-driven development: an industrial


case study," Software Quality Journal, Volume 19 Issue 4, pp. 643-661, 2011.

[35] The Eclipse Foundation, https://www.eclipse.org/home/index.php, Re-


trieved on 2014-09-11.

[36] JetBrains, http://www.jetbrains.com/idea/, Retrieved on 2014-09-11.

[37] Free Software Foundation, http://www.gnu.org/software/emacs/, Retrieved


on 2014-09-11.

[38] Phil Hagelberg, http://leiningen.org, Retrieved on 2014-09-11.

[39] Kodowa, http://www.lighttable.com, Retrieved on 2014-09-11.

[40] M. Fogus and C. Houser, https://github.com/joyofclojure/book-source,


Retrieved on 2014-09-11.

[41] H. Duncan, https://github.com/pallet/ritz, Retrieved on 2014-09-11.

45
www.kth.se

You might also like