# NAME
Spek
# SYNOPSIS
Test centric web framework
# INSTALL
cpanm Spek
# USAGE
## declare http endpoints
To declare http endpoint you should write swat tests:
### GET /users
To get users list ...
$ mkdir users
$ touch users/get.txt
### POST /user
To create a new user ...
$ mkdir -p user
$ touch user/post.txt
### GET /user
To get user info ...
$ mkdir -p user/id
$ touch user/id/get.txt
Follow [swat](https://github.com/melezhik/swat) for full explanation of swat test harness.
## run spek application
$ spek
... you should see:
HTTP::Server::PSGI: Accepting connections at http://0:5000/
## run swat tests
# in parallel console
$ echo 127.0.0.1:5000 > host
$ swat
... you should get an errors like <HTTP/1.0 404 Not Found>
... as endpoints are not *defined* yet
127.0.0.1 - - [16/Mar/2016:09:00:03 +0300] "GET /users HTTP/1.1" 404 20 "-" "curl/7.47.0-DEV"
127.0.0.1 - - [16/Mar/2016:09:00:03 +0300] "GET /user/foo HTTP/1.1" 404 20 "-" "curl/7.47.0-DEV"
127.0.0.1 - - [16/Mar/2016:09:00:03 +0300] "POST /user HTTP/1.1" 404 20 "-" "curl/7.47.0-DEV"
## implement endpoints
### initialize application
app.pm :
our $USERS = {
'alex' => {
'age' => 39,
'email' => 'melezhik@gmail.com'
},
'bot' => {
'age' => 1000,
'email' => 'iamarobot@...'
},
};
### populate controllers
It should be [Kelp](https://metacpan.org/pod/Kelp) routes:
#### GET users/
users/get.pm :
my ( $self ) = @_;
my $list;
for my $id ( sort keys %$USERS ) {
$list.="$id: $USERS->{$id}->{email}\n";
}
$list;
#### GET user/id
user/id/get.pm :
my ($self, $id ) = @_;
"id: $id email: ".($USERS->{$id}->{email});
#### POST user
user/post.pm
my ( $self ) = @_;
$USERS->{$self->param('name')} = {
email => $self->param('email'),
age => $self->param('age'),
};
"user ".($self->param('name'))." created OK"
## refine your tests
### Tests should be swat modules:
$ echo swat_module=1 > users/swat.ini
$ echo swat_module=1 > user/id/swat.ini
$ echo swat_module=1 > user/swat.ini
### Some tests should pass http request parameters:
#### POST /user
$ echo 'curl_params="-d name=foo -d age=30 -d email='foo@bar'"' >> user/swat.ini
### Define expected response:
#### POST /user :
$ echo 'user foo created OK' > user/post.txt
#### GET /users :
$ cat users/get.txt
alex: melezhik@gmail.com
bot: iamarobot@...
foo: foo@bar
#### GET /user/id
$ cat user/id/get.txt
id: foo email: foo@bar
## And finally create a meta story:
$ mkdir crud
$ cat crud/meta.txt
application should be able
to perform CRUD operations
$ cat crud/hook.pm
run_swat_module( POST => '/user' );
run_swat_module( GET => '/user/id', { id => 'foo' } );
run_swat_module( GET => '/users' );
## Rebuild spek app
In console running spek app:
$ <CTRL> + <C>
$ spek
... should see:
reiniting spek app ...
populate app.pm ...
populate post /home/vagrant/my/spek-example-app/user ...
populate get /home/vagrant/my/spek-example-app/user/id ...
populate get /home/vagrant/my/spek-example-app/users ...
HTTP::Server::PSGI: Accepting connections at http://0:5000/
## Run tests
Now you have a specifi(K)ation simply just running:
$ swat
... should see:
/home/vagrant/.swat/.cache/5738/prove/crud/META/request.t ..
# @META
# application should be able
# to perform CRUD operations
#
ok 1 - 200 / 1 of 2 curl -X POST -k --connect-timeout 20 -m 20 -d name=foo -d age=30 -d email='foo@bar' -L -D - '127.0.0.1:5000/user'
ok 2 - output match 'user foo created OK'
ok 3 - 200 / 1 of 2 curl -X GET -k --connect-timeout 20 -m 20 -L -D - '127.0.0.1:5000/user/foo'
ok 4 - output match 'id: foo email: foo@bar'
ok 5 - 200 / 1 of 2 curl -X GET -k --connect-timeout 20 -m 20 -L -D - '127.0.0.1:5000/users'
ok 6 - output match 'alex: melezhik@gmail.com'
ok 7 - output match 'bot: iamarobot@...'
ok 8 - output match 'foo: foo@bar'
1..8
ok
All tests successful.
Files=1, Tests=8, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.06 cusr 0.00 csys = 0.08 CPU)
Result: PASS
# Application introspection
## routes
To inspect all available routes:
$ spek --routes
## meta stories
To inspect meta stories:
$ spek --meta
Read [swat](https://github.com/melezhik/swat#meta-stories) documentation to know more about swat meta stories.
# Advanced usage
## handling json responses
Modify Kelp controller
### GET /user/id
$ cat user/id/get.pm
my ($self, $id ) = @_;
{
id => $id,
email => $USERS->{$id}->{email} ,
age => $USERS->{$id}->{age}
}
Add json handler to swat test:
$ cat user/id/get.handler
my $headers = shift;
my $body = shift;
use JSON;
$hash = decode_json($body);
return "$headers\n".("id: $hash->{id}\nemail: $hash->{email}\nage: $hash->{age}");
Regenerate spek application
$ <CTRL> + <C> # to stop running application
$ spek
... should see:
reiniting spek app ...
populate app.pm ...
populate post /home/vagrant/my/spek-example-app/user ...
populate delete /home/vagrant/my/spek-example-app/user/id ...
populate get /home/vagrant/my/spek-example-app/user/id ...
populate get /home/vagrant/my/spek-example-app/users ...
generate hook for /user/id ...
inject response handler for get ...
Run swat tests
$ swat
... should see:
ok 1 - 200 / 1 of 1 curl -X DELETE -k --connect-timeout 20 -m 20 -L -D - '127.0.0.1:5000/user/foo'
# modified response saved to /home/vagrant/.swat/.cache/23224/prove/mFRKPZV6Im
ok 2 - output match 'id: foo'
ok 3 - output match 'message: user deleted OK'
ok 4 - output match 'status: OK'
Read more [swat docs](https://github.com/melezhik/swat#process-http-responses) on how to process
http responses.
To add handlers for other http methods simple create a proper handler file:
$ DELETE /user/id
$ nano user/id/delete.handler # and so on ...
# Author
[Alexey Melezhik](mailto:melezhik@gmail.com)
# Download example spek application
You can upload source code here - [https://github.com/melezhik/spek-example-app](https://github.com/melezhik/spek-example-app)
# See also
* [Kelp](https://metacpan.org/pod/Kelp)
* [swat](https://metacpan.org/pod/swat)