is:
The use of code to prove that other code is correct.
Our Software
Our Tests
All Together Now: micro-research problem
Use google to find out:
What are some pros and cons of test driven development. Everything is a tradeoff, so expect to find some of both.
There are two main categories that tests help with
There are two main categories that tests can hinder
(is that even a word?)
In this case, tests are helping us move code from "in development" to "in production" faster
Quickly Verify that new code didn't break old features. These are called "regression" bugs
Our New Code
Existing Tests
We can use tools to gate new code entering the master branch based on a test suite
Our New Code
Github hook
git push origin master
Existing Tests
Push accepted
Push rejected
Operational benefits are mostly for the business. There are lots of other ways to uses tests for this
Tests also help developers write better code.
In order to test our software, the tests need to know about the software. This is good, because it mandates that our code is "portable".
Our Software
Our Tests
Our Software
Our Tests
module.exports = {
funcOne: funOne,
funcTwo: funTwo
// ...
}
var ourModule =
require('../ourModule');
One benefit of testing is that we know we can use the modules from at least one other context
This means good tests should FORCE our code to be portable / testable in a vacuum
Our Software
Our Tests
describe('Our function', function(){
it('Should have describable behavior', function() {
expect(add(2, 2)).to.equal(4);
});
});
Tests force us to describe the our expectations.
describe('Our function', function(){
it('Should have describable behavior', function() {
expect(add(2, 2)).to.equal(4);
});
});
This means good tests make our expectations about the code explicit
Simply writing descriptions of what the code MUST do helps us write better code.
Codifying the meaning of our description with code allows us to make those descriptions more explicit.
describe("parensChecker", function() {
it('Should return(true) for valid nested parens', function() {
// ...
});
it('Should return(false) for invalid nested parens', function() {
// ...
});
it('Should gracefully return(false) for all invalid inputs', function(){
// ...
});
Sure, but what does "valid" and "invalid" really mean?
describe("parensChecker", function() {
it('Should return true if input is a string representing properly' +
' nested and aligned parenthesis, brackets or curly braces: (){}[]', function() {
expect(parensChecker('{}')).to.equal(true);
// ...
});
it('Should return false if there are an odd number of parens', function() {
expect(parensChecker('())').to.equal(false);
// ...
});
it('Should return false if the opening and closing braces do not match in type', function(){
expect(parensChecker('(]')).to.equal(false);
// ...
});
it('Should return false if the input is not a string'), function() {
expect(parensChecker(123)).to.equal(false);
// ...
});
it('Should return false if the input is a string, but contains anything other than ' +
'the allowed character set of {}[]()', function() {
expect(parensChecker('123').to.equal(false);
// ...
});
});
it('Should return(true) for valid nested parens', function() {
// Simple cases - if these break nothing will work.
expect(parensChecker("[]")).to.equal(true);
expect(parensChecker("()")).to.equal(true);
expect(parensChecker("{}")).to.equal(true);
// Mixing cases - test clean combinations which
// will be easier to debug when they break.
expect(parensChecker("()[]{}")).to.equal(true);
expect(parensChecker("({[]})")).to.equal(true);
expect(parensChecker("{[()]}")).to.equal(true);
expect(parensChecker("[({})]")).to.equal(true);
// More complex cases - sometimes even randomly
// generated cases - the goal of which is to ensure
// any edge case is found.
expect(parensChecker("[][][]{}(){[]}({})")).to.equal(true);
expect(parensChecker("([([[{(){}[()]}]])])")).to.equal(true);
});
it('Should return(true) for valid nested parens', function() {
// Simple cases - if these break nothing will work.
expect(parensChecker("[]")).to.equal(true);
expect(parensChecker("()")).to.equal(true);
expect(parensChecker("{}")).to.equal(true);
// Mixing cases - test clean combinations which
// will be easier to debug when they break.
expect(parensChecker("()[]{}")).to.equal(true);
expect(parensChecker("({[]})")).to.equal(true);
expect(parensChecker("{[()]}")).to.equal(true);
expect(parensChecker("[({})]")).to.equal(true);
// More complex cases - sometimes even randomly
// generated cases - the goal of which is to ensure
// any edge case is found.
expect(parensChecker("[][][]{}(){[]}({})")).to.equal(true);
expect(parensChecker("([([[{(){}[()]}]])])")).to.equal(true);
});
Do you think you would really have bothered to use your code with:
"([([[{(){}[()]}]])])"
it('Should return false if the input is a string, but contains anything ' +
'other than the allowed character set of {}[]()', function() {
expect(parensChecker('123').to.equal(false);
expect(parensChecker('{}()[]fail').to.equal(false);
expect(parensChecker('').to.equal(false);
expect(parensChecker('\n').to.equal(false);
expect(parensChecker('\r').to.equal(false);
});
What about these failure cases?
This means good tests use the code in all the ways it might be used.
It means a good test hits all the "code paths"
it('Should return(true) for valid nested parens', function() {
// Simple cases - if these break nothing will work.
expect(parensChecker("[]")).to.equal(true);
expect(parensChecker("()")).to.equal(true);
expect(parensChecker("{}")).to.equal(true);
});
Before we write our function, lets define some very BASIC success criteria
Write a function which can pass the first 3 tests!
it('Should return(true) for valid nested parens', function() {
// Mixing cases - test clean combinations which
// will be easier to debug when they break.
expect(parensChecker("()[]{}")).to.equal(true);
expect(parensChecker("({[]})")).to.equal(true);
expect(parensChecker("{[()]}")).to.equal(true);
expect(parensChecker("[({})]")).to.equal(true);
});
Add some more tests, these may work, they may fail.
Update the code until it passes the old tests, and the new tests
Write a test which fails
Fix the code so it passes
Keep the test passing, make stylistic changes
Repeat
Database
Our Codebase
Our new feature
Internet Services
Environment Stuff
Database
Our Codebase
Our new feature
Internet Services
Environment Stuff
Database
Our Codebase
Our new feature
Internet Services
Environment Stuff
Database
Our Codebase
Our new feature
Internet Services
Environment Stuff