1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
|
Now that we have our aggregator, we have to write our tests. For this I
will use Test::Class. Ovid have wrote an
[[http://www.modernperlbooks.com/mt/2009/03/organizing-test-suites-with-testclass.html][excellent]]
[[http://www.modernperlbooks.com/mt/2009/03/reusing-test-code-with-testclass.html][serie]]
[[http://www.modernperlbooks.com/mt/2009/03/making-your-testing-life-easier.html][of]]
[[http://www.modernperlbooks.com/mt/2009/03/using-test-control-methods-with-testclass.html][articles]]
[[http://www.modernperlbooks.com/mt/2009/03/working-with-testclass-test-suites.html][about
Test::Class]]. You should really read this, because I will not enter in
details.
We have two things to test:
- roles
- aggregator
*** Roles
For this, we create the following files:
- t/tests/Test/TestObject.pm
- t/tests/Test/MyRoles.pm
- t/tests/Test/MyAggregator.pm
- t/run.t
We will write our *run.t*:
#+BEGIN_SRC perl
use lib 't/test';
use Test::MyRoles;
Test::Class->runtests;
#+END_SRC
this test load our tests and run them.
This is a just a class for the tests, that load our 2 roles.
now the roles' tests:
#+BEGIN_SRC perl
package Test::MyRoles;
use strict;
use warnings;
use base 'Test::Class';
use Test::Exception;
use Test::More;
sub class {'Test::TestObject'}
sub url {"http://lumberjaph.net/blog/index.php/feed/"}
sub startup : Tests(startup => 1) {
my $test = shift;
use_ok $test->class, "use ok";
`rm -rf /tmp/FileCache/myaggregator/`;
}
sub constructor : Tests(1) {
my $test = shift;
can_ok $test->class, 'new';
}
sub fetch_feed : Tests(5) {
my $test = shift;
can_ok $test->class, 'fetch_feed';
ok my $obj = $test->class->new(), '... object is created';
my $res = $obj->fetch_feed($test->url);
is $res->code, "200", "... fetch is a success";
like $res->content, qr/lumberjaph/, "... and content is good";
# now data should be in cache
my $ref = $obj->cache->get($test->url);
ok defined $ref, "... url is now in cache";
$res = $obj->fetch_feed($test->url);
is $res->code, "304", "... already in cache";
}
sub feed_parser : Tests(3) {
my $test = shift;
can_ok $test->class, 'feed_parser';
my $ua = LWP::UserAgent->new;
my $res = $ua->get($test->url);
ok my $obj = $test->class->new(), "... object is created";
my $feed = $obj->feed_parser(\$res->content);
isa_ok $feed, "XML::Feed::Format::RSS";
}
1;
#+END_SRC
As you can see, some methods have an attribute, which indicate this
method as a test method.
The startup method is run as the first method each time the tests are
executed. In our case, we test if we can load our class, and we delete
the cache of the aggregator.
We have a "constructor" test, that check we can do a new on our class.
Now we have to tests our 2 methods from the roles. We will test the
fetch\_feed method first.
First, we indicate the number of tests that will be executed (6 in our
case). Then we can write the test in the method:
- create an object
- fetch an url, and test the HTTP code of the response
- check if the content look like something we want
- now the data should be in cache, and the a new fetch of the url
should return a 304 HTTP code
The second method to test is feed\_parser. This method will do 3 tests.
- create an object
- we manually fetch the content from a feed
- send this content to feed\_parser
- the result should return a XML::Feed::Format::RSS object
When you run the tests now =prove t/run.t=
the following result is produced:
#+BEGIN_SRC perl
t/run.t ..
1..11
ok 1 - use Test::TestObject;
#
# Test::MyRoles->constructor
ok 2 - Test::TestObject->can('new')
#
# Test::MyRoles->feed_parser
ok 3 - Test::TestObject->can('feed_parser')
ok 4 - ... object is created
ok 5 - The object isa XML::Feed::Format::RSS
#
# Test::MyRoles->fetch_feed
ok 6 - Test::TestObject->can('fetch_feed')
ok 7 - ... object is created
ok 8 - ... fetch is a success
ok 9 - ... and content is good
ok 10 - ... url is now in cache
ok 11 - ... already in cache
ok
All tests successful.
Files=1, Tests=11, 3 wallclock secs ( 0.03 usr 0.01 sys + 0.66 cusr 0.09 csys = 0.79 CPU)
Result: PAS
#+END_SRC
*** Aggregator
As we have our tests for the roles, we can write the tests for the
aggregator now. First, we add a new line in *t/run.t*.
#+BEGIN_SRC perl
use Test::MyAggregator
#+END_SRC
We edit our *t/tests/Test/MyAggregator.pm*:
#+BEGIN_SRC perl
package Test::MyAggregator;
use strict;
use warnings;
use base 'Test::Class';
use Test::Exception;
use Test::More;
sub class {'MyAggregator'}
sub context {
{ dsn => 'dbi:SQLite:dbname=/tmp/myaggregator.db',
kioku_dir => 'dbi:SQLite:/tmp/mykioku.db',
};
}
sub startup : Tests(startup => 2) {
my $test = shift;
use_ok $test->class, "use ok";
`touch /tmp/myaggregator.db`;
my $context = $test->context;
my $dsn = $context->{dsn};
my $schema = MyModel->connect($dsn);
$schema->deploy;
ok $schema->resultset('Feed')->create(
{ feedid => 1,
url => 'http://lumberjaph.net/blog/index.php/feed/',
}
),
"... insert one feed in the db";
}
sub shutdown : Tests(shutdown => 2) {
my $test = shift;
ok unlink '/tmp/myaggregator.db', '... unlink db test';
ok unlink '/tmp/mykioku.db', '... unlink kioku test';
}
sub constructor : Tests(1) {
my $test = shift;
can_ok $test->class, 'new';
}
sub dedupe_feed : Tests(4) {
my $test = shift;
my $context = $test->context;
my $ua = LWP::UserAgent->new;
my $res = $ua->get("http://lumberjaph.net/blog/index.php/feed/");
ok my $obj = $test->class->new(context => $context),
"... MyAggregator created";
$obj->dedupe_feed($res, 1);
my $schema = MyModel->connect($context->{dsn});
is $schema->resultset('Entry')->search()->count, 10,
'... 10 entries in the db';
my $first = $schema->resultset('Entry')->search()->first;
my $res_kiokudb;
$obj->kioku->txn_do(
scope => 1,
body => sub {
$res_kiokudb = $obj->kioku->lookup($first->id);
}
);
ok $res_kiokudb, '... got an object';
is $res_kiokudb->permalink, $first->permalink, '... content is valid';
}
1;
#+END_SRC
The startup test create a database from our model, and insert a feed.
The shutdown test remove the 2 database that we will use (MyModel and
kiokudb).
The dedupe\_feed is really simple. We create a MyAggregator object,
fetch a rss feed manually, and dedup the result. Now we check the result
in the MyModel database: do we have 10 entries ? if it's the case, we
are good. We fetch a result from this db, and check if it's also present
in KiokuDB, and if the permalink is the same for the two. So with 4
tests, we do a simple check of our class.
Execute the tests (you can comment the roles' tests in run.t):
#+BEGIN_SRC perl
t/run.t ..
1..9
ok 1 - use MyAggregator;
ok 2 - ... insert one feed in the db
#
# Test::MyAggregator->constructor
ok 3 - MyAggregator->can('new')
#
# Test::MyAggregator->dedupe_feed
ok 4 - ... MyAggregator created
ok 5 - ... 10 entries in the db
ok 6 - ... got an object
ok 7 - ... content is valid
ok 8
ok 9
ok
All tests successful.
Files=1, Tests=9, 3 wallclock secs ( 0.01 usr 0.01 sys + 1.39 cusr 0.12 csys = 1.53 CPU)
Result: PASS
#+END_SRC
We have our tests, so next step is the Catalyst frontend. As for the
precedents parts,
[[http://git.lumberjaph.net/p5-ironman-myaggregator.git/][the code is
available on my git server]].
|