Software Engineering Lab.
Spring 2017
7. Push changes for the Integration Machine to be built tested again
8. Wait for test results
9. Fix the conflicts if step 9 fails
10. It's Friday. Go home.
This example might seem both naive and overcomplicated.
Let's look at some practices used in adopting this testing habit.
A good way to catch bugs more quickly and efficiently is to include automated tests in the build process.
The rise of TDD and XP has had a great impact on what's called Self-Testing code/build
They both emphasize: Writing test before code
We have weaker requirements for Self-Testing code:
Good coverage - Simple to run - Embeddable in build process
Integration is primarily about communication and responsibility
Integration allows developers to tell other developers about the changes they have made. Frequent communication allows people to know quickly as changes develop.
By keeping the time between changes short, bugs are easier to find. Changes are not widespread
"The key to fixing problems quickly is finding them quickly"
The more frequently you commit, the less places you have to look for conflict errors, and the more rapidly you fix conflicts.
Frequent commits encourage developers to break down their work into small chunks of a few hours each
Using daily commits, a team gets frequent tested builds. This ought to mean that the mainline stays in a healthy state. In practice, however, things still do go wrong.
An untested commit
Development machine variation
As a result, each commit should be tested on a separate machine (aka. Continuous Integration Server)
a CI server is a monitor over the mainline source code
Checkout a new version after every commit, build, test, notify the developer
Scheduled builds and tests
The mainline is the Holy Grail of the development focus
If it fails against a commit, it should be fixed immediately
Most of the time, due to the short gap between current commit and the last one, the reason is obvious
Might lead to reverting the mainline and giving the commit a second thought.
"nobody has a higher priority task than fixing the build" -- Kent Beck
Not everyone needs to stop doing what they do and struggle to fix the build
It further encourages strong communication and collaboration for a unified goal
The whole point of Continuous Integration is to provide rapid feedback
At most, 10 minutes build time is within reason
Most small projects will build within minutes, but not enterprise applications
End to End testing
Service Discovery Scenarios
DB testing: inserting and modifying millions of records
Load Testing: benchmarking server's response time under a huge load for a long time
An accepted solution is to have multiple build stages
Stage 1:
a commit will be tested agains fast Unit Tests
The mainline is updated for other developers. The end product might not. he or she can go home afterwards
Stage 2:
Longs tests will run later, perhaps in parallel
Bugs caught by stage n should be transformed into small chunks of fast test and migrated into stage m, where m < n
Tests must progress/improve through time
CI Server was introduced for primarily two reasons:
Avoid dependency on development environment
24/7 monitoring over the mainline
Further emphasis on this leads to striving to duplicate everything from production machine.
Might not always be possible. Mimicking every single parameter of the production environment is time consuming (about which we talked in #7)
Nowadays, most CI servers do this up to a certain degree
One of the most important things to communicate is the state of the mainline build
Most open source projects have multiple github badges
Some companies that use internal source control and CI servers change the ambient of their room based on build status.
Making everyone involved encourages them to stay involved with the project (simple, yet effective Gamification)
Text
language: node_js
node_js:
- "7"
Text
Text
language: node_js
node_js:
- "7"
install: ./install-dependencies.sh
// or
install:
- bundle install --path vendor/bundle
- npm install
script: ./custom-test.sh
// or
script:
- mytest --run
- npm test
install: ./foo.sh
before_script:
- apt-get install redis
- redis-server
after_success:
- ./yoohoo.sh
after_failure:
- ./revert-all.sh
Hooks can be added to different phases of the build process
Install
script
deploy:
provider: npm
after_deploy: ./update-doc.sh
before_deploy: ./clean-up.sh
Optional deploys can be added using Continues Providers (npm)
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq [packages list]
Packages can / should be installed using of the hooks depending on type
# blocklist
branches:
except:
- legacy
- experimental
# safelist
branches:
only:
- master
- stable
git commit -am "this will be ignored [ci skip]"
git commit -am "also this [skip ci]"
language: ruby
rvm:
- 1.9.3
- 2.0.0
- 2.1.0
env:
- DB=mongodb
- DB=redis
- DB=mysql
gemfile:
- Gemfile
- gemfiles/rails4.gemfile
- gemfiles/rails31.gemfile
- gemfiles/rails32.gemfile
matrix:
exclude:
- rvm: 2.0.0
gemfile: Gemfile
matrix:
exclude:
- rvm: 2.0.0
gemfile: Gemfile
env: DB=mongodb
- rvm: 2.0.0
gemfile: Gemfile
env: DB=redis
- rvm: 2.0.0
gemfile: Gemfile
env: DB=mysql
Text