Skip to content

Instantly share code, notes, and snippets.

@Anna-Myzukina
Last active September 26, 2018 21:21
Show Gist options
  • Save Anna-Myzukina/b3ffd40b84142ce050b093e8cddbaa0c to your computer and use it in GitHub Desktop.
Save Anna-Myzukina/b3ffd40b84142ce050b093e8cddbaa0c to your computer and use it in GitHub Desktop.
test-anything
//Exercise 1
var emotify = require(process.argv[2])
console.log(emotify(process.argv[3]))
/*
# Log it out
Developing apps and modules is fun. However often you might be concerned whether
things work or when you add new features you want to be sure you did not break
anything. Therefore developers invented tests for their well-being. They allow
you to automatically, well, test your application or module.
Let's assume you wrote a function called emotify, which takes a String and
adds a space and a :) to it. How would you check that your function is
working?
Maybe your first idea was calling the function with a value and console.log
the result and then check its output in the console.
var emotify = require('./emotify.js')
console.log(emotify('just testing'))
Try this yourself. We are going to provide the location for the awesome
emotify module in process.argv[2] and the String for the test in
process.argv[3].*/
//Exercise 2
var isCoolNumber = require(process.argv[2]);
var assert = require('assert');
assert.ok(isCoolNumber(42),"OK!");
/**
# Tell me what's wrong
Write a passing assertion for the function isCoolNumber, that will assure that
it returns true when passing 42 in it.
The path of the module exporting the function will be provided through
process.argv[2].
------------------------------------------------------------------------------------------------------------
## Hints
Well this was probably nothing new for you. But wait don't leave, we are going
to learn about some better ways to do this.
If you are wondering, what's wrong about console.log, then think about this:
If your functions are going to be more complex it is going to be harder and
harder to actually read the output. You have to know what output is expected for
every input and for different functions.
So it would be better if our tests only told us about whether something works or
not. Surely we could probably test each output with !== and warn if something
is wrong like this.
if(add(2,1) !== 3) throw new Error('add(2,1) should be 3')
Now we get an error every time something is wrong, with the message what's not
working. However in node there is a nice built-in module for this called
assert.
var assert = require('assert')
assert(add(2,1) === 3,'add(2,1) should be 3')
Or as alternatively:
assert.deepEqual(add(2,1), 3, 'add(2,1) should be 3')
Here are some functions you can use with assert. For a full list, see the
documentation.
assert.ok(value, message) // tests if value is truthy
assert.equal(actual, expected, message) // ==
assert.notEqual(actual, expected, message) // !=
assert.deepEqual(actual, expected, message) // for comparing objects
assert.notDeepEqual(actual, expected, message)
assert.strictEqual(actual, expected, message) // ===
assert.notStrictEqual(actual, expected, message) // !==
## Resources
* Node documentation: http://nodejs.org/api/assert.html
*/
//Exercise 3
var test = require('tape'),
fancify = require(process.argv[2]);
test('fancify function', function(t) {
t.equal(fancify('Hello'), '~*~Hello~*~', 'wraps string in ~*~~*~');
t.equal(fancify('Hello', true), '~*~HELLO~*~', 'converts the string into ALLCAPS, wraps in ~*~~*~');
t.equal(fancify('Hello', false, '!'), '~!~Hello~!~', 'optional argument that determines the character');
t.end();
});
/*
# Tape it together
Write tests that output TAP, that tests the following properties of a function
fancify. The function will be provided in process.argv[2].
1 fancify(str) returns the str wrapped in ~*~
Example: fancify('Hello') returns ~*~Hello~*~
2 It takes an optional second argument that converts the string into ALLCAPS
Example: fancify('Hello', true) returns ~*~HELLO~*~
3 It takes a third optional argument that determines the character in the middle
Example: fancify('Hello', false, '!') returns ~!~Hello~!~
## Hints
Testing with assert still has some downsides. Even though we don't have to
check all the values ourself like in the first level, but now we only get not
very readable errors when something is wrong. Otherwise our tests don't do
anything. Maybe we still would like to see some information that everything is
ok.
There is a standard for outputting data from tests called TAP, the
Test Anything Protocol. It is nicely readable for humans as well as for our
robotic friends.
One module for testing that outputs TAP is tape (another one is tap, duh).
It takes a description of what you are testing and a callback function, with a
parameter t that works quite similar to assert. You use it to write your
assertions. However it also has a function t.end(), that you call when you are
done with your assertions.
The tape module is not included in Node, so you need to install them in your
project folder (where you keep your exercise files) with npm install tape.
Here is an example how to test the last function with tape
var test = require('tape')
var isCoolNumber = require('./cool.js')
test('isCoolNumber accepts only cool numbers', function (t) {
t.ok(isCoolNumber(42), '42 should be cool')
t.end()
})
## Resources
* TAP on Wikipedia http://en.wikipedia.org/wiki/Test_Anything_Protocol
* The tape module https://www.npmjs.org/package/tape*/
//Exercise 4
var test = require('tape'),
repeatCallback = require(process.argv[2]);
test('repeatCallback function', function(t) {
t.plan(4);
repeatCallback(4, function() {
t.pass('Callback called successfully');
});
});
/*
# Call me maybe
Write a test for a function repeatCallback(n, cb), that calls the callback
cb exactly n times. n can be any number you want in your test code.
As before the functions location will be provided through process.argv[2].
## Hints
Sometimes we are not simply checking return values of functions. A lot in
JavaScript and node happens through callbacks and events. For this we often want
to know: Was that callback called or not?
The event-driven nature of JavaScript is also the reason why we had to call the
t.end() function in the last level. The test has to know whether we are done.
However there is maybe a better way to do this with callbacks using t.plan(n).
When we call this in the beginning we can tell tape how many assertions we are
doing.
var test = require('tape')
test('nextTick', function (t) {
t.plan(1)
process.nextTick(function () {
t.pass('callback called')
})
})
In this example we only have one callback, which will simply pass the test when
it is called. So we could have used t.end() within the callback instead.
However you might see, that if we had multiple callbacks in our tests the
t.plan(n) would come in handy.
*/
//Exercise 5
var tape = require('tape'),
feedCat = require(process.argv[2]);
tape('feedCat function', function(t) {
t.plan(3);
t.equal(feedCat('fish'), 'yum', 'will return str "yum"');
t.equal(feedCat('chicken'), 'yum', 'will return str "yum"');
t.throws(function() {
feedCat('chocolate');
});
});
/*
# To err is human
A function feedCat takes any kind of food as a String argument and returns
'yum' for everything you feed them. However if you try to feed the cat
'chocolate', the function will throw an error.
Write tests for feedCat to be sure kittens can be fed yummy food without
being harmed.
The function will be provided through process.argv[2].
## Hints
To err is human, to purr feline. - Robert Byrne
Chocolate is awesome and so are cats. However they do not make a wonderful
combination. The Caffeine and Theobromine in the chocolate can harm cats as well
as dogs.
Feeding chocolate to cats would therefore be considered an error. One way in
JavaScript to deal with errors is to throw them (even though in Node this is
probably not the best way).
If we want to deal with these errors, we can use try and catch like this:
try {
petDog('bordercollie')
}
catch(err) {
console.error('It seems like it doesn\'t like that.')
}
When we test things, we often say that we want to make sure that there are no
errors. Well, that is not entirely true. We certainly want error-free code.
However if someone else tries to do something weird with our functions, it
still might be good to see an error. So good that we might want to test this
behavior, e.g. to make sure there is no chocolate fed to cats.
So maybe we know that a dachshund does not like to be petted. Well we could test
this behavior like this:
t.throws(function () {
petDog('dachshund')
})
Now the test expects an error and throws an error if there is no error. Mind
boggling, right?
By the way, if you are familiar with functional javascript, you might already
know that you could also write it in one line:
t.throws(petDog.bind(null, 'dachhund'))
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment