Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
A unit testing framework in a tweet.
<?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);}
@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Feb 17, 2014

Output:

✔︎ It should sum two numbers
✘ It should display an X for a failing test

The script will end with exit code 1 for failures.

Owner

mathiasverraes commented Feb 17, 2014

Output:

✔︎ It should sum two numbers
✘ It should display an X for a failing test

The script will end with exit code 1 for failures.

@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Feb 17, 2014

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

"about 15 - 50 errors per 1000 lines of delivered code."

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.

Owner

mathiasverraes commented Feb 17, 2014

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

"about 15 - 50 errors per 1000 lines of delivered code."

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.

@marcaube

This comment has been minimized.

Show comment Hide comment
@marcaube

marcaube Feb 17, 2014

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);}

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);}
@Xeoncross

This comment has been minimized.

Show comment Hide comment
@Xeoncross

Xeoncross Feb 19, 2014

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 (\033[0m).

<? function it($m,$p){echo "\033[".($p?"32m✔":"31m✘")." It $m\033[0m\n"; if(!$p)$GLOBALS['f']=1;}function done({if(@$GLOBALS['f'])die(1);}

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 (\033[0m).

<? function it($m,$p){echo "\033[".($p?"32m✔":"31m✘")." It $m\033[0m\n"; if(!$p)$GLOBALS['f']=1;}function done({if(@$GLOBALS['f'])die(1);}
@Xeoncross

This comment has been minimized.

Show comment Hide comment
@Xeoncross

Xeoncross Feb 19, 2014

If you want to get really sloppy you can register a chain of N shutdown functions for a slim 120 characters.

<?php function it($m,$p){echo "\033[".($p?"32m✔":"31m✘")." It $m\033[0m\n"; if(!$p)register_shutdown_function('die',1);}

Update: Actually, that doesn't work. It doesn't like 'die' as a callback function for some reason. Here is the 132 character version:

<?php function it($m,$p){echo "\033[".($p?"32m✔":"31m✘")." It $m\033[0m\n"; if(!$p)register_shutdown_function(function(){die(1);});}

If you want to get really sloppy you can register a chain of N shutdown functions for a slim 120 characters.

<?php function it($m,$p){echo "\033[".($p?"32m✔":"31m✘")." It $m\033[0m\n"; if(!$p)register_shutdown_function('die',1);}

Update: Actually, that doesn't work. It doesn't like 'die' as a callback function for some reason. Here is the 132 character version:

<?php function it($m,$p){echo "\033[".($p?"32m✔":"31m✘")." It $m\033[0m\n"; if(!$p)register_shutdown_function(function(){die(1);});}
@Xeoncross

This comment has been minimized.

Show comment Hide comment
@Xeoncross

Xeoncross Feb 19, 2014

For those that would rather not write a description for each test you can try this 199 character beast that reports the tests verbatim:

function test($p){
    $t=debug_backtrace(0)[0];$m=substr(trim(file($t['file'])[$t['line']-1]),5,-2);
    echo "\033[".($p?"32m✔":"31m✘")." $m\033[0m\n";
    if(!$p)register_shutdown_function(function(){die(1);});
}

test(1+1==2);
test(1+1==3);

Produces:

✔ 1+1==2
✘ 1+1==3

Requires PHP 5.4 to use the array dereferencing.

For those that would rather not write a description for each test you can try this 199 character beast that reports the tests verbatim:

function test($p){
    $t=debug_backtrace(0)[0];$m=substr(trim(file($t['file'])[$t['line']-1]),5,-2);
    echo "\033[".($p?"32m✔":"31m✘")." $m\033[0m\n";
    if(!$p)register_shutdown_function(function(){die(1);});
}

test(1+1==2);
test(1+1==3);

Produces:

✔ 1+1==2
✘ 1+1==3

Requires PHP 5.4 to use the array dereferencing.

@ciaranmcnulty

This comment has been minimized.

Show comment Hide comment
@ciaranmcnulty

ciaranmcnulty Feb 24, 2014

I know you were going for the constraint rather than 'smallest possible' but I trimmed it to 108 chars:

function it($m,$p){echo $p?'✔︎':'✘'," It $m\n"; @$GLOBALS[f]&=!!!$p;}function done(){@$GLOBALS[f]?:die(1);}

I know you were going for the constraint rather than 'smallest possible' but I trimmed it to 108 chars:

function it($m,$p){echo $p?'✔︎':'✘'," It $m\n"; @$GLOBALS[f]&=!!!$p;}function done(){@$GLOBALS[f]?:die(1);}
@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Mar 2, 2014

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:

✔︎ It should pass when the expected exception is thrown
✘ It should fail when the wrong kind of exception is thrown
✘ It should fail when no exception thrown
Owner

mathiasverraes commented Mar 2, 2014

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:

✔︎ It should pass when the expected exception is thrown
✘ It should fail when the wrong kind of exception is thrown
✘ It should fail when no exception thrown
@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Mar 2, 2014

Also, you guys rock!

Owner

mathiasverraes commented Mar 2, 2014

Also, you guys rock!

@turanct

This comment has been minimized.

Show comment Hide comment
@turanct

turanct Mar 3, 2014

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:

✔︎ It should pass when the expected interface is implemented
✔︎ It should pass when the expected interface is implemented
✘ It should fail when the expected interface is not implemented
✘ It should fail when the given object doesn't exist.

turanct commented Mar 3, 2014

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:

✔︎ It should pass when the expected interface is implemented
✔︎ It should pass when the expected interface is implemented
✘ It should fail when the expected interface is not implemented
✘ It should fail when the given object doesn't exist.
@SpacefulSpecies

This comment has been minimized.

Show comment Hide comment
@turanct

This comment has been minimized.

Show comment Hide comment
@turanct

turanct Mar 3, 2014

@SpacefulSpecies, too bad that class_implements doesn't throw exceptions, it shouts warnings at you when you give it an erronious class name... You can then indeed use the ugly @-way to create a function doing the same thing as what i posted before, while getting it to fit in a tweet, like this:

<?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.

turanct commented Mar 3, 2014

@SpacefulSpecies, too bad that class_implements doesn't throw exceptions, it shouts warnings at you when you give it an erronious class name... You can then indeed use the ugly @-way to create a function doing the same thing as what i posted before, while getting it to fit in a tweet, like this:

<?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.

@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Mar 7, 2014

but i'd really rather not use the @ sign to suppress errors.

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?

Owner

mathiasverraes commented Mar 7, 2014

but i'd really rather not use the @ sign to suppress errors.

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?

@turanct

This comment has been minimized.

Show comment Hide comment
@turanct

turanct Mar 8, 2014

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.

turanct commented Mar 8, 2014

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.

@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Mar 16, 2014

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!!

Owner

mathiasverraes commented Mar 16, 2014

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!!

@adambrett

This comment has been minimized.

Show comment Hide comment
@adambrett

adambrett Mar 20, 2014

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:

Method someMethod("someValue") from Mockery_0_SomeInterface should be called
 exactly 1 times but called 0 times.
✘ It should use SomeInterface to do Something

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:

Method someMethod("someValue") from Mockery_0_SomeInterface should be called
 exactly 1 times but called 0 times.
✘ It should use SomeInterface to do Something
@adambrett

This comment has been minimized.

Show comment Hide comment
@adambrett

adambrett Mar 24, 2014

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:

Code Coverage:

/path/to/file.php: 1/2 lines

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');

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:

Code Coverage:

/path/to/file.php: 1/2 lines

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');
@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Jun 5, 2014

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:

✘ It should do a bunch of calculations
Process finished with exit code 1
Owner

mathiasverraes commented Jun 5, 2014

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:

✘ It should do a bunch of calculations
Process finished with exit code 1
@anthonysterling

This comment has been minimized.

Show comment Hide comment
@anthonysterling

anthonysterling Jun 5, 2014

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();

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();
@samdbeckham

This comment has been minimized.

Show comment Hide comment
@samdbeckham

samdbeckham Jun 6, 2014

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();

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();
@anthonysterling

This comment has been minimized.

Show comment Hide comment
@anthonysterling

anthonysterling Jun 6, 2014

😄

😄

@marcaube

This comment has been minimized.

Show comment Hide comment
@marcaube

marcaube Jun 6, 2014

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!

marcaube commented Jun 6, 2014

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!

@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Jun 28, 2014

Installation guide with composer

  1. Remove composer.
  2. Go to https://gist.github.com/mathiasverraes/9046427
  3. Copy/paste the entire #TestFrameworkInATweet sourcecode into your own code somewhere.
  4. Write a test.
  5. Get a PHP Fatal error: Call to undefined function it()
  6. Curse. (Or find a more senior developer to teach you some appropriate curses).
  7. Mess around with require_once and relative paths and directory separators and autoloaders for a bit.
  8. Oh look it's beer o'clock, maybe try again on Monday.
Owner

mathiasverraes commented Jun 28, 2014

Installation guide with composer

  1. Remove composer.
  2. Go to https://gist.github.com/mathiasverraes/9046427
  3. Copy/paste the entire #TestFrameworkInATweet sourcecode into your own code somewhere.
  4. Write a test.
  5. Get a PHP Fatal error: Call to undefined function it()
  6. Curse. (Or find a more senior developer to teach you some appropriate curses).
  7. Mess around with require_once and relative paths and directory separators and autoloaders for a bit.
  8. Oh look it's beer o'clock, maybe try again on Monday.
@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@everzet

This comment has been minimized.

Show comment Hide comment
@everzet

everzet Jul 8, 2014

Implementation

<?php

function it($m,$p){echo"\033[3",$p?'2m✔︎':'1m✘'.register_shutdown_function(function(){die(1);})," It $m\033[0m\n";}
  • Colours
  • Auto-exit
  • 121 char

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);
  • Framework auto-update built in.

everzet commented Jul 8, 2014

Implementation

<?php

function it($m,$p){echo"\033[3",$p?'2m✔︎':'1m✘'.register_shutdown_function(function(){die(1);})," It $m\033[0m\n";}
  • Colours
  • Auto-exit
  • 121 char

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);
  • Framework auto-update built in.
@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Sep 4, 2014

Another article

It also covers some minor frameworks like phpspec and phpunit, but of course the focus is mostly about #TestFrameworkInATweet!

Owner

mathiasverraes commented Sep 4, 2014

Another article

It also covers some minor frameworks like phpspec and phpunit, but of course the focus is mostly about #TestFrameworkInATweet!

@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Sep 12, 2014

@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Oct 6, 2014

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

Owner

mathiasverraes commented Oct 6, 2014

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

@turanct

This comment has been minimized.

Show comment Hide comment
@turanct

turanct Oct 7, 2014

And a REPL in a tweet: gist / tweet

turanct commented Oct 7, 2014

And a REPL in a tweet: gist / tweet

@liuggio

This comment has been minimized.

Show comment Hide comment
@liuggio

liuggio Oct 22, 2014

framework components in tweets https://github.com/liuggio/sized140

liuggio commented Oct 22, 2014

framework components in tweets https://github.com/liuggio/sized140

@lastguest

This comment has been minimized.

Show comment Hide comment
@lastguest

lastguest Oct 30, 2014

78 chars

function it($m,$p){echo"\033[3",$p?"2m✔":"1m✘"," It $m\033[0m\n";$p||die(1);}

78 chars

function it($m,$p){echo"\033[3",$p?"2m✔":"1m✘"," It $m\033[0m\n";$p||die(1);}
@jm42

This comment has been minimized.

Show comment Hide comment
@jm42

jm42 Oct 31, 2014

Dependency injection container in a tweet ... https://gist.github.com/jm42/3c32dd50bb9d09f57c4a

jm42 commented Oct 31, 2014

Dependency injection container in a tweet ... https://gist.github.com/jm42/3c32dd50bb9d09f57c4a

@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Nov 9, 2017

We have a glorious 280 characters at our disposal now. https://twitter.com/mathiasverraes/status/928526123297894400
We can bloat this framework with features. Go wild.

Owner

mathiasverraes commented Nov 9, 2017

We have a glorious 280 characters at our disposal now. https://twitter.com/mathiasverraes/status/928526123297894400
We can bloat this framework with features. Go wild.

@brzuchal

This comment has been minimized.

Show comment Hide comment
@brzuchal

brzuchal Nov 9, 2017

I just pushed few moments ago modified one with adjusted to 280 chars tweet and with some colors and dropped a need for done() function call at end. https://twitter.com/mbrzuchalski/status/928539030035320832
It has also some debug info about last error:

<?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";});

brzuchal commented Nov 9, 2017

I just pushed few moments ago modified one with adjusted to 280 chars tweet and with some colors and dropped a need for done() function call at end. https://twitter.com/mbrzuchalski/status/928539030035320832
It has also some debug info about last error:

<?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";});
@brzuchal

This comment has been minimized.

Show comment Hide comment
@brzuchal

brzuchal Nov 9, 2017

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:

✔︎ It should pass
✘ It should fail
ERROR /home/brzuchal/test-oneliner.php@6
✘ It should also fail
ERROR /home/brzuchal/test-oneliner.php@7
FAIL

Passing example:

it('should pass', true);

Results:

✔︎ It should pass
OK

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);

brzuchal commented Nov 9, 2017

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:

✔︎ It should pass
✘ It should fail
ERROR /home/brzuchal/test-oneliner.php@6
✘ It should also fail
ERROR /home/brzuchal/test-oneliner.php@7
FAIL

Passing example:

it('should pass', true);

Results:

✔︎ It should pass
OK

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);
@mathiasverraes

This comment has been minimized.

Show comment Hide comment
@mathiasverraes

mathiasverraes Nov 9, 2017

nice one :)

Owner

mathiasverraes commented Nov 9, 2017

nice one :)

@gmazzap

This comment has been minimized.

Show comment Hide comment
@gmazzap

gmazzap Nov 29, 2017

I gave up to colors, but

  • use standard output/error for the success/failure output
  • add some mimimum formatting
  • add possibility to pass a callback as predicate
<?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:

✘ It should display an X for a failing test. FAIL: /path/to/file.php #11
✔ It should append to an ArrayIterator.
✔ It should pass test for InvalidArgumentException exception.

gmazzap commented Nov 29, 2017

I gave up to colors, but

  • use standard output/error for the success/failure output
  • add some mimimum formatting
  • add possibility to pass a callback as predicate
<?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:

✘ It should display an X for a failing test. FAIL: /path/to/file.php #11
✔ It should append to an ArrayIterator.
✔ It should pass test for InvalidArgumentException exception.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment