<?php | |
require_once 'TestFrameworkInATweet.php'; | |
it("should sum two numbers", 1+1==2); | |
it("should display an X for a failing test", 1+1==3); | |
done(); |
<?php | |
function it($m,$p){echo ($p?'✔︎':'✘')." It $m\n"; if(!$p){$GLOBALS['f']=1;}}function done(){if(@$GLOBALS['f'])die(1);} |
This comment has been minimized.
This comment has been minimized.
Should you use this in production?Let's throw statistics at the problem, so we don't have to do any actual thinking. According to Code Complete, the industry average is
Let's be pessimistic and say that #TestFrameworkInATweet contains 50/1000 = 0.05 bugs. This is of course UNACCEPTABLE. However, as a testing framework, it should help you to dramatically decrease the defect rate of your code. So let's say you write one line of code, with an expected avg(0.015, 0.05) = 0.0325 bugs. Using #TestFrameworkInATweet's awesome bug reduction capacity, you've now reduced the expectancy to 0.0001 bugs. Add that to #TestFrameworkInATweet's own 0.05 bugs, you have increased your system from 0.0325 bugs to 0.0501 bugs total, simply by introducing unit testing! But now say you have a system with two lines of code, your expected bug count drops from 0.0325*2 = 0.065 total to 0.0502 total bugs. With a massive system of 5 LoC, you'll go from 0.1625 to 0.0505 bugs, which is a31% improvement. Awesome! Conclusion: The logic is infallible. #TestFrameworkInATweet only benefits large systems of >= 2 lines of code. |
This comment has been minimized.
This comment has been minimized.
If you don't echo anything after your assertions you can even add some colours, 137 chars total. <?php
function it($m,$p){echo "\033[".($p?"32m✔":"31m✘")." It $m\n"; if(!$p){$GLOBALS['f']=1;}}function done(){if(@$GLOBALS['f'])die(1);} |
This comment has been minimized.
This comment has been minimized.
Remove the brackets around the the global assignment and use short open tags. The code is still is only 139 characters even while adding the closing color code (
|
This comment has been minimized.
This comment has been minimized.
If you want to get really sloppy you can register a chain of N shutdown functions for a slim 120 characters.
Update: Actually, that doesn't work. It doesn't like 'die' as a callback function for some reason. Here is the 132 character version:
|
This comment has been minimized.
This comment has been minimized.
For those that would rather not write a description for each test you can try this 199 character beast that reports the tests verbatim:
Produces:
Requires PHP 5.4 to use the array dereferencing. |
This comment has been minimized.
This comment has been minimized.
I know you were going for the constraint rather than 'smallest possible' but I trimmed it to 108 chars:
|
This comment has been minimized.
This comment has been minimized.
I wrote an exception matching plugin in a tweet: <?php
function throws($exp,Closure $cb){try{$cb();}catch(Exception $e){return $e instanceof $exp;}return false;} Usage: <?php
it("should pass when the expected exception is thrown",
throws("InvalidArgumentException", function () {
// The SUT throws an exception
throw new InvalidArgumentException;
}));
it("should fail when the wrong kind of exception is thrown",
throws("InvalidArgumentException", function () {
throw new RuntimeException;
}));
it("should fail when no exception thrown",
throws("InvalidArgumentException", function () {
// no op
})); Produces:
|
This comment has been minimized.
This comment has been minimized.
Also, you guys rock! |
This comment has been minimized.
This comment has been minimized.
Interface matching, but couldn't get it to fit in a tweet when stripping it down... :-( maybe one of you can? <?php
/**
* Matcher for interfaces
*
* @param string $interface The interface name
* @param mixed $object A valid classname or an object
*
* @return bool
*/
function hasInterface($interface, $object)
{
try {
$reflection = new ReflectionClass($object);
} catch (Exception $e) {
return false;
}
return in_array((string) $interface, $reflection->getInterfaceNames());
} Usage: <?php
it("should pass when the expected interface is implemented",
hasInterface('Countable', new RecursiveArrayIterator(array()))
);
it("should pass when the expected interface is implemented",
hasInterface('Countable', 'RecursiveArrayIterator')
);
it("should fail when the expected interface is not implemented",
hasInterface('Countable', new RecursiveDirectoryIterator(__DIR__))
);
it("should fail when the given object doesn't exist.",
hasInterface('Countable', '')
); Produces:
|
This comment has been minimized.
This comment has been minimized.
@turanct this function might help http://be2.php.net/manual/en/function.class-implements.php |
This comment has been minimized.
This comment has been minimized.
@SpacefulSpecies, too bad that <?php
function hasInterface($i, $o){$is=@class_implements($o, false);return in_array((string) $i, (array) $is);} but i'd really rather not use the @ sign to suppress errors. |
This comment has been minimized.
This comment has been minimized.
Wait a minute. You're trying to write to write good code. In a tweet. In a plugin for a piece of code that uses globals. :-O Also, why do you want to test on interfaces? |
This comment has been minimized.
This comment has been minimized.
Those are all valid points :-) maybe that was too eager of me. Isn't it practical or useful in some cases to test on interfaces? For instance to describe the identity of your class before you start writing it? I could be wrong but i thought phpspec had something like it, and it looked like a fun challenge to make it work here too. |
This comment has been minimized.
This comment has been minimized.
PHPSpec has it as a default, but if you watch presentations by Marcello, it's the first thing he removes :). There are rare case where you'd want to test if an object implements a certain interface. TestFrameworkAsATweet has a built-in feature to deal with that, but it is undocumented and untested at this time. Use at your own peril! <?php
it("should implement the Awesome interface", $object instanceof Awesome); Bam! TestFrameworkInATweet has everything! Please test thoroughly and submit bug reports for any issues you might find. Who needs bloated frameworks like phpspec? That has like, I don't know, probably MORE THAN 100 LINES OF CODE!!!11!! |
This comment has been minimized.
This comment has been minimized.
134 character plugin to add Mockery support to TestFrameworkInATweet: <?php
function withMock(Closure $cb){$cb();try{Mockery::close();}catch(Exception $e){echo $e->getMessage()."\n";return false;}return true;} Usage: <?php
use Mockery as m;
it('should use SomeInterface to do Something', withMock(function () {
$mock = m::mock('SomeInterface');
$mock->shouldReceive('someMethod')
->with('someValue')
->once()
->andReturn(true);
$sut = new SystemToTest($mock);
$sut->test();
}); Output:
|
This comment has been minimized.
This comment has been minimized.
Code-coverage support in 144 characters! <?php
function cc($c){foreach($c as $f=>$z){
$s=file($f);$r=0;foreach($s as $l=>$x){if(isset($z[++$l])){($z[$l]==1)&&$r++;}}yield [$f,$r,count($z)];}} Usage: <?php
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
it('should do stuff here', true);
foreach (cc(xdebug_get_code_coverage()) as list($file, $run, $total)) {
echo "$file: $run/$total lines\n";
} Some cavats: PHP 5.5+ only, not much use on its own needs as it needs an output function too, and it includes code coverage in test and vendor files. Here's an example text report function in 116 chars: <?php
function cc_text($c) {echo "Code Coverage: \n\n";foreach($c as list($f,$r,$t)){echo "$f: $r/$t lines\n";}echo "\n";} Usage: <?php
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
it('should do stuff here', true);
cc_text(cc(xdebug_get_code_coverage()); Output:
I reality, you need to filter out test and vendor files, and would like to output in a single function, but I couldn't fit that in a tweet, here's a slightly longer version (185 chars) that does that: <?php
function cc($c,$p){foreach($c as $f=>$z){if(preg_match($p,$f))continue;
$s=file($f);$r=0;foreach($s as $l=>$x){if(isset($z[++$l])){($z[$l]==1)&&$r++;}}$z=count($z);echo "$f: $r/$z\n";}} Usage: <?php
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
it('should do stuff here', true);
cc(xdebug_get_code_coverage(), '/test|vendor/i'); |
This comment has been minimized.
This comment has been minimized.
Multiple assertions in one go: <?php
function all(array $ps){return array_reduce($ps,function($a,$p){return $a&&$p;},true);}
// usage
it("should do a bunch of calculations", all([
1+1 == 2,
1+2 == 1249,
]);
done(); Output:
|
This comment has been minimized.
This comment has been minimized.
Slightly shorter. <?php
function all($ps){return $ps===array_filter($ps);}
it('should be shorter than Mathias\' proposal and still work', all([
strlen('function all($ps){return $ps===array_filter($ps);}') < strlen('function all(array $ps){return array_reduce($ps,function($a,$p){return $a&&$p;},true);}'),
1+1 === 2,
]));
done(); |
This comment has been minimized.
This comment has been minimized.
Schoolboy error there Sterling. <?php
function all($a){return $a===array_filter($a);}
it('should be shorter than Sterling\'s proposal by three characters and still work', all([
(strlen('function all($ps){return $ps===array_filter($ps);}') - strlen('function all($a){return $a===array_filter($a);}')) === 3,
1+2 === 3,
]));
done(); |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
In a way, this whole exercise is reminiscent of those C/Perl code obfuscation contests. It's pretty crafty what one can do in 140 chars. Pretty awesome! |
This comment has been minimized.
This comment has been minimized.
TestFrameworkInATweet, used in the wild: http://stackoverflow.com/questions/24109521/youtube-playlist-all-videos-duration-show-in-php/24214755#24214755 |
This comment has been minimized.
This comment has been minimized.
Installation guide with composer
|
This comment has been minimized.
This comment has been minimized.
Rocking the unit testing world: http://php-and-symfony.matthiasnoback.nl/2014/07/descriptive-unit-tests/ |
This comment has been minimized.
This comment has been minimized.
Implementation<?php
function it($m,$p){echo"\033[3",$p?'2m✔︎':'1m✘'.register_shutdown_function(function(){die(1);})," It $m\033[0m\n";}
Installation guide:<?php
eval(file_get_contents('https://gist.githubusercontent.com/everzet/8a14043d6a63329cee62/raw/twest.php'));
it('sums up two numbers', 1+1==2);
it('displays an X for a failing test', 1+1==3);
|
This comment has been minimized.
This comment has been minimized.
It also covers some minor frameworks like phpspec and phpunit, but of course the focus is mostly about #TestFrameworkInATweet! |
This comment has been minimized.
This comment has been minimized.
Real life usage: http://stackoverflow.com/a/24214755 |
This comment has been minimized.
This comment has been minimized.
Soon we will have full stack frameworks in a tweet! Here is a prioritized event dispatcher in a tweet: https://gist.github.com/xsist10/824b559c4effaf43ddb3 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
framework components in tweets https://github.com/liuggio/sized140 |
This comment has been minimized.
This comment has been minimized.
78 chars
|
This comment has been minimized.
This comment has been minimized.
Dependency injection container in a tweet ... https://gist.github.com/jm42/3c32dd50bb9d09f57c4a |
This comment has been minimized.
This comment has been minimized.
We have a glorious 280 characters at our disposal now. https://twitter.com/mathiasverraes/status/928526123297894400 |
This comment has been minimized.
This comment has been minimized.
I just pushed few moments ago modified one with adjusted to 280 chars tweet and with some colors and dropped a need for <?php
function it($m,$p){echo($p?"\e[0;32m✔︎":"\e[0;31m✘")."\e[0m It $m\n";if(!$p){$GLOBALS['d']=debug_backtrace();}}
register_shutdown_function(function(){@$d=$GLOBALS['d'][0];if($d){echo "\e[1;37;41mFailed in {$d['file']} at {$d['line']}\e[0m\n";die(1);}echo "\e[1;32mOK\e[0m\n";}); |
This comment has been minimized.
This comment has been minimized.
Now there's even more, added error file and line every failed test and short summary at shutdown: <?php
function it($m,$p){echo"\e[3".($p?"2m✔︎":"1m✘")."\e[0m It $m\n";if(!$p){$GLOBALS['e']=1;$d=debug_backtrace()[0];echo"ERROR {$d['file']}@{$d['line']}\n";}}register_shutdown_function(function(){echo"\e[1;3".(($e=@$GLOBALS['e'])?"7;41mFAIL":"2mOK")."\e[0m\n";die($e);}); Failing example: it('should pass', true);
it('should fail', false);
it('should also fail', false); Results:
Passing example: it('should pass', true); Results:
And here is a link to my fork of this gist https://gist.github.com/brzuchal/5aeec672207bc9df4898ba99d6f7b369#file-testframeworkinatweet-php So if someone would wanan use it can just: <?php
include 'https://gist.githubusercontent.com/brzuchal/5aeec672207bc9df4898ba99d6f7b369/raw/22ce43d813007323e7cf1cee6a101f1ce7674466/TestFrameworkInATweet.php'
it('should pass', true);
it('should fail', false);
it('should also fail', false); |
This comment has been minimized.
This comment has been minimized.
nice one :) |
This comment has been minimized.
This comment has been minimized.
I gave up to colors, but
<?php
function it($m,$p){ $d=debug_backtrace(0)[0];
is_callable($p) and $p=$p();
global $e;$e=$e||!$p;
$o=($p?"✔":"✘")." It $m";
fwrite($p?STDOUT:STDERR,$p?"$o\n":"$o FAIL: {$d['file']} #{$d['line']}\n");
}
register_shutdown_function(function(){global $e; $e and die(1);}); Example: it('should display an X for a failing test.', 1+1===3);
it('should append to an ArrayIterator.', function() {
$iterator = new ArrayIterator();
$iterator->append('test');
return count($iterator) === 1 && $iterator->current() === 'test';
});
it('should pass test for InvalidArgumentException exception.', function() {
try {
throw new InvalidArgumentException();
} catch(InvalidArgumentException $e) {
return true;
}
}); with output:
|
This comment has been minimized.
Output:
The script will end with exit code 1 for failures.