Skip to content

Commit fedda7a

Browse files
authored
Merge pull request #28 from BinaryStudioAcademy/feature/comment-tests
Feature/comment tests
2 parents 43359cf + 9c9b7e4 commit fedda7a

File tree

8 files changed

+359
-8
lines changed

8 files changed

+359
-8
lines changed

.travis.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ services:
66
language: php
77

88
php:
9-
- 7.1
109
- 7.2
1110

1211
cache:
@@ -22,13 +21,11 @@ install:
2221
- composer install --no-interaction --prefer-source
2322

2423
before_script:
25-
- cp .env.travis .env
26-
- php artisan key:generate
27-
- php artisan jwt:secret -f
24+
- cp .env.travis .env
2825
- php artisan migrate --force
2926

3027
script:
31-
- vendor/bin/phpunit --coverage-clover=coverage.xml
28+
- vendor/bin/phpunit --coverage-clover=coverage.xml -c phpunit.travis.xml
3229

3330
after_success:
3431
- bash <(curl -s https://codecov.io/bash)

backend/.env.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,8 @@ PUSHER_APP_CLUSTER=mt1
4343
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
4444
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
4545

46-
JWT_SECRET=
46+
JWT_SECRET=
47+
48+
MYSQL_PORT=33061
49+
APP_PORT=7777
50+
MYSQL_PORT_TEST_DB=33062

backend/.env.testing

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
APP_KEY=base64:70WifS7kyzGyovfVSvZdMIpkQu6lWda873VoTQc0BEg=
2+
3+
DB_CONNECTION=mysql
4+
DB_HOST=mysql-testing
5+
DB_PORT=3306
6+
DB_DATABASE=thread-testing
7+
DB_USERNAME=user
8+
DB_PASSWORD=secret
9+
10+
JWT_SECRET=somesecret

backend/.env.travis

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
APP_ENV=testing
2-
APP_KEY=
2+
APP_KEY=base64:70WifS7kyzGyovfVSvZdMIpkQu6lWda873VoTQc0BEg=
33

44
DB_CONNECTION=mysql
55
DB_HOST=127.0.0.1
@@ -8,4 +8,4 @@ DB_DATABASE=thread
88
DB_USERNAME=root
99
DB_PASSWORD=
1010

11-
JWT_SECRET=
11+
JWT_SECRET=somesecret

backend/docker-compose.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,16 @@ services:
3333
- MYSQL_DATABASE=thread
3434
- MYSQL_USER=user
3535
- MYSQL_PASSWORD=secret
36+
37+
mysql-testing:
38+
image: mysql:5.7.22
39+
container_name: thread-mysql-testing
40+
volumes:
41+
- ./docker/mysql:/etc/mysql/conf.d
42+
ports:
43+
- "${MYSQL_PORT_TEST_DB}:3306"
44+
environment:
45+
- MYSQL_ROOT_PASSWORD=root
46+
- MYSQL_DATABASE=thread-testing
47+
- MYSQL_USER=user
48+
- MYSQL_PASSWORD=secret

backend/phpunit.travis.xml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit backupGlobals="false"
3+
backupStaticAttributes="false"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
convertErrorsToExceptions="true"
7+
convertNoticesToExceptions="true"
8+
convertWarningsToExceptions="true"
9+
processIsolation="false"
10+
stopOnFailure="false">
11+
<testsuites>
12+
<testsuite name="Unit">
13+
<directory suffix="Test.php">./tests/Unit</directory>
14+
</testsuite>
15+
16+
<testsuite name="Feature">
17+
<directory suffix="Test.php">./tests/Feature</directory>
18+
</testsuite>
19+
</testsuites>
20+
<filter>
21+
<whitelist processUncoveredFilesFromWhitelist="true">
22+
<directory suffix=".php">./app</directory>
23+
</whitelist>
24+
</filter>
25+
<php>
26+
<server name="APP_ENV" value="testing"/>
27+
<server name="APP_KEY" value="base64:70WifS7kyzGyovfVSvZdMIpkQu6lWda873VoTQc0BEg="/>
28+
<server name="BCRYPT_ROUNDS" value="4"/>
29+
<server name="CACHE_DRIVER" value="array"/>
30+
<server name="MAIL_DRIVER" value="array"/>
31+
<server name="QUEUE_CONNECTION" value="sync"/>
32+
<server name="SESSION_DRIVER" value="array"/>
33+
<server name="DB_CONNECTION" value="mysql"/>
34+
<server name="DB_HOST" value="127.0.0.1"/>
35+
<server name="DB_PORT" value="3306"/>
36+
<server name="DB_DATABASE" value="thread"/>
37+
<server name="DB_USERNAME" value="root"/>
38+
<server name="DB_PASSWORD" value=""/>
39+
<server name="JWT_SECRET" value="somesecret"/>
40+
</php>
41+
</phpunit>
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
<?php
2+
3+
namespace Tests\Feature\Api;
4+
5+
use App\Entity\Comment;
6+
use App\Entity\Tweet;
7+
use App\Entity\User;
8+
use App\Exceptions\ErrorCode;
9+
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
10+
use Illuminate\Foundation\Testing\RefreshDatabase;
11+
use Illuminate\Contracts\Auth\Authenticatable;
12+
use Illuminate\Support\Facades\Auth;
13+
use Tests\CreatesApplication;
14+
15+
abstract class ApiTestCase extends BaseTestCase
16+
{
17+
use CreatesApplication;
18+
use RefreshDatabase;
19+
20+
protected const AUTH_PREFIX = 'Bearer';
21+
22+
protected const ROOT_RESPONSE_KEY = 'data';
23+
24+
/**
25+
* @var array
26+
*
27+
* User response item structure
28+
*/
29+
protected const USER_RESOURCE_STRUCTURE = [
30+
'id',
31+
'name',
32+
'nickname',
33+
'email',
34+
'avatar'
35+
];
36+
37+
/**
38+
* @var array
39+
*
40+
* Comment response item structure
41+
*/
42+
protected const COMMENT_RESOURCE_STRUCTURE = [
43+
'id',
44+
'body',
45+
'author_id',
46+
'created_at',
47+
'updated_at',
48+
'author' => self::USER_RESOURCE_STRUCTURE
49+
];
50+
51+
/**
52+
* @var string
53+
*/
54+
private $jwtToken;
55+
56+
protected function setUp(): void
57+
{
58+
parent::setUp();
59+
60+
$this->seedFakeData();
61+
}
62+
63+
protected function seedFakeData(int $itemsAmount = 2): void
64+
{
65+
factory(User::class, $itemsAmount)->create();
66+
factory(Tweet::class, $itemsAmount)->create();
67+
factory(Comment::class, $itemsAmount)->create();
68+
}
69+
70+
/**
71+
* Override call method
72+
*
73+
* Appends jwt auth header
74+
*
75+
* @param string $method
76+
* @param string $uri
77+
* @param array $parameters
78+
* @param array $cookies
79+
* @param array $files
80+
* @param array $server
81+
* @param null $content
82+
* @return \Illuminate\Foundation\Testing\TestResponse
83+
*/
84+
public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
85+
{
86+
if ($this->jwtToken) {
87+
// append token auth header
88+
$this->withHeader('AUTHORIZATION', self::AUTH_PREFIX . " {$this->jwtToken}");
89+
}
90+
91+
return parent::call($method, $uri, $parameters, $cookies, $files, $server, $content);
92+
}
93+
94+
private function authenticate(Authenticatable $user): void
95+
{
96+
// Auth::login() returns jwt token from JWTGuard class
97+
$this->jwtToken = Auth::login($user);
98+
}
99+
100+
protected function actingWithToken(Authenticatable $user = null) : self
101+
{
102+
$user = $user ?? factory(User::class)->create();
103+
104+
$this->authenticate($user);
105+
106+
return $this->actingAs($user, 'api');
107+
}
108+
109+
private function assertUriIsValid(string $uri): void
110+
{
111+
if (empty($uri)) {
112+
throw new \InvalidArgumentException('Request URI cannot be empty.');
113+
}
114+
}
115+
116+
protected function assertCollectionResponse(string $uri, array $jsonStructure, array $queryParams = []): void
117+
{
118+
$this->assertUriIsValid($uri);
119+
120+
$response = $this->call('GET', $uri, $queryParams)->assertOk();
121+
122+
// assert response data key doesn't contain empty array
123+
$this->assertNotEmpty($response->json('data'));
124+
125+
$response->assertJsonStructure([
126+
// * means to assert each array item for same structure
127+
self::ROOT_RESPONSE_KEY => ['*' => $jsonStructure]
128+
]);
129+
}
130+
131+
protected function assertCollectionErrorResponse(string $uri, array $queryParams = []): void
132+
{
133+
$this->assertUriIsValid($uri);
134+
135+
$this->call('GET', $uri, $queryParams)->assertJsonStructure(['errors' => []]);
136+
}
137+
138+
protected function assertItemResponse(string $uri, array $jsonStructure): void
139+
{
140+
$this->assertUriIsValid($uri);
141+
142+
$this->get($uri)
143+
->assertOk()
144+
->assertJsonStructure([self::ROOT_RESPONSE_KEY => $jsonStructure]);
145+
}
146+
147+
protected function assertNotFoundResponse(string $uri): void
148+
{
149+
$this->assertUriIsValid($uri);
150+
151+
$this->get($uri)
152+
->assertNotFound()
153+
->assertExactJson([
154+
'errors' => [
155+
[
156+
'code' => ErrorCode::NOT_FOUND,
157+
'message' => 'Resource not found.'
158+
]
159+
]
160+
]);
161+
}
162+
163+
protected function assertErrorResponse(string $uri, array $attributes = [], string $httpMethod = 'POST'): void
164+
{
165+
$this->assertUriIsValid($uri);
166+
167+
$this->json($httpMethod, $uri, $attributes)
168+
->assertStatus(400)
169+
->assertJsonStructure(['errors' => []]);
170+
}
171+
172+
protected function assertCreatedResponse(string $uri, array $attributes): void
173+
{
174+
$this->assertUriIsValid($uri);
175+
$this->assertAttributesIsValid($attributes);
176+
177+
$this->json('POST', $uri, $attributes)
178+
->assertStatus(201)
179+
->assertJsonStructure(['data' => ['id']]);
180+
}
181+
182+
protected function assertUpdatedResponse(string $uri, array $attributes): void
183+
{
184+
$this->assertUriIsValid($uri);
185+
$this->assertAttributesIsValid($attributes);
186+
187+
$this->json('PUT', $uri, $attributes)->assertStatus(200);
188+
}
189+
190+
protected function createResourceItemUri(string $uri, int $id): string
191+
{
192+
$this->assertUriIsValid($uri);
193+
194+
return $uri . '/' . $id;
195+
}
196+
197+
private function assertAttributesIsValid(array $attributes): void
198+
{
199+
if (empty($attributes)) {
200+
throw new \InvalidArgumentException('Request attributes are empty.');
201+
}
202+
}
203+
}

0 commit comments

Comments
 (0)