I have been reading up on TDD and it's something I genuinely want to learn and be good at. This is something I haven't been doing as a developer and I thought it's about time I get into it.
These are two custom helper Facade classes in a Laravel 5.5 REST API project:
XmlXsdValidator Facade - https://pastebin.com/Nc7hsjic
S3 Facade - https://pastebin.com/tMxprRy2
Below are two Unit tests I've wrote and they run fine:
I am looking for criticism and tips on how to write better tests, which in turn will help me write good/readable/re-usable/maintainable code.
XmlXsdValidatorFacadeTest:
<?php
namespace Tests\Unit;
use Tests\TestCase;
use ArgumentCountError;
use InvalidArgumentException;
use UnexpectedValueException;
use App\Facades\XmlXsdValidator;
class XmlXsdValidatorFacadeTest extends TestCase
{
protected function setUp()
{
parent::setUp();
$this->validXml = file_get_contents(storage_path('data/test/Valid_ListBucketResult.xml'));
$this->invalidXml = file_get_contents(storage_path('data/test/Invalid_ListBucketResult.xml'));
$this->xsdPath = config('myApp.listBucketResultXsdPath');
}
public function testShouldCheckIfAllArgumentsArePresent()
{
$this->expectException(ArgumentCountError::class);
XmlXsdValidator::validate();
}
public function testShouldCheckIfXmlStringArgumentIsPresent()
{
$this->expectException(InvalidArgumentException::class);
XmlXsdValidator::validate('', null);
}
public function testShouldCheckIfXsdSchemaPathArgumentIsPresent()
{
$this->expectException(InvalidArgumentException::class);
XmlXsdValidator::validate(null, '');
}
public function testShouldCheckIfInvalidXmlStringIsSupplied()
{
$this->expectException(UnexpectedValueException::class);
XmlXsdValidator::validate('invalid-data', $this->xsdPath);
}
public function testShouldFailXmlXsdValidationWhenXmlDataDoesNotMatchXsdSchema()
{
$this->expectException(UnexpectedValueException::class);
XmlXsdValidator::validate($this->invalidXml, $this->xsdPath);
}
public function testShouldSucceedXmlXsdValidationWhenXmlDataMatchesXsdSchema()
{
$this->assertTrue(XmlXsdValidator::validate($this->validXml, $this->xsdPath));
}
}
S3FacadeTest:
<?php
namespace Tests\Unit;
use App\Facades\S3;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;
use InvalidArgumentException;
class S3FacadeTest extends TestCase
{
protected function setUp()
{
parent::setUp();
$this->originalConfig = config('myApp');
$this->testCustomerFiles = [
'a4a06bb0-3fbe-40bd-9db2-f68354ba742f.json',
'be0438bf-8b0d-4c57-913d-fcafb0bb41f0.json',
'be9b2a8b-e846-4365-8d5f-0fca4ef9aefb.json',
];
$this->testCustomerGuid = 'a4a06bb0-3fbe-40bd-9db2-f68354ba742f';
$this->testCustomerAccounts = [
'861fc585-3313-4928-891d-c8711dfe3f8a',
'0391a1cc-2c00-47b8-9880-aa9fe96bef51',
'8a28f09a-c234-4a95-b1e0-cdbc68979d0a'
];
}
public function testShouldCheckIfValidS3BucketUrlIsPresentInApplicationConfig()
{
$this->expectException(InvalidArgumentException::class);
config(['myApp.s3BucketUrl' => '']);
S3::listCustomerFiles();
}
public function testShouldReturnCachedArrayOfCustomerFilesInCorrectFormat()
{
config($this->originalConfig);
$customerFiles = S3::listCustomerFiles();
$this->assertTrue(is_array($customerFiles));
$this->assertTrue($this->arrays_are_similar($customerFiles, array_flip($this->testCustomerFiles)));
$this->assertCount(3, $customerFiles);
foreach ($this->testCustomerFiles as $customerFile) {
$this->assertArrayHasKey($customerFile, $customerFiles);
}
$this->assertTrue(Cache::has('Customer_Files'));
$cachedCustomerFiles = Cache::get('Customer_Files');
$this->assertTrue(is_array($cachedCustomerFiles));
$this->assertTrue($this->arrays_are_similar($cachedCustomerFiles, array_flip($this->testCustomerFiles)));
$this->assertCount(3, $cachedCustomerFiles);
foreach ($this->testCustomerFiles as $customerFile) {
$this->assertArrayHasKey($customerFile, $cachedCustomerFiles);
}
}
public function testShouldReturnNonCachedArrayOfCustomerFilesInCorrectFormat()
{
Cache::clear();
config($this->originalConfig);
config(['myApp.cacheMins' => 0]);
$customerFiles = S3::listCustomerFiles();
$this->assertTrue(is_array($customerFiles));
$this->assertTrue($this->arrays_are_similar($customerFiles, array_flip($this->testCustomerFiles)));
$this->assertCount(3, $customerFiles);
foreach ($this->testCustomerFiles as $customerFile) {
$this->assertArrayHasKey($customerFile, $customerFiles);
}
$this->assertFalse(Cache::has('Customer_Files'));
}
public function testShouldReturnCachedArrayOfCustomerAccountsInCorrectFormat()
{
config($this->originalConfig);
$customerAccounts = S3::loadCustomerAccounts($this->testCustomerGuid);
$this->assertTrue(is_array($customerAccounts));
$this->assertCount(100, $customerAccounts);
foreach ($this->testCustomerAccounts as $customerAccountGuid) {
$this->assertTrue(in_array($customerAccountGuid, $customerAccounts));
}
$this->assertTrue(Cache::has(sprintf('Customer_%s_Accounts', $this->testCustomerGuid)));
$cachedCustomerAccounts = Cache::get(sprintf('Customer_%s_Accounts', $this->testCustomerGuid));
foreach ($cachedCustomerAccounts as $cachedCustomerAccountGuid) {
$this->assertTrue(in_array($cachedCustomerAccountGuid, $cachedCustomerAccounts));
$this->assertTrue(Cache::has(sprintf('Account_%s_Data', $cachedCustomerAccountGuid)));
$cachedAccountData = Cache::get(sprintf('Account_%s_Data', $cachedCustomerAccountGuid));
$this->assertTrue(is_object($cachedAccountData));
$this->assertObjectNotHasAttribute('id', $cachedAccountData);
$this->assertObjectHasAttribute('firstname', $cachedAccountData);
$this->assertObjectHasAttribute('lastname', $cachedAccountData);
$this->assertObjectHasAttribute('email', $cachedAccountData);
$this->assertObjectHasAttribute('telephone', $cachedAccountData);
$this->assertObjectHasAttribute('balance', $cachedAccountData);
}
}
public function testShouldReturnNonCachedArrayOfCustomerAccountsInCorrectFormat()
{
Cache::clear();
config($this->originalConfig);
config(['myApp.cacheMins' => 0]);
$customerAccounts = S3::loadCustomerAccounts($this->testCustomerGuid);
$this->assertTrue(is_array($customerAccounts));
$this->assertCount(100, $customerAccounts);
foreach ($this->testCustomerAccounts as $customerAccountGuid) {
$this->assertTrue(in_array($customerAccountGuid, $customerAccounts));
}
$this->assertFalse(Cache::has(sprintf('Customer_%s_Accounts', $this->testCustomerGuid)));
}
/**
* Determine if two associative arrays are similar.
* @author https://stackoverflow.com/a/3843768/2332336
*
* @param array $a
* @param array $b
* @return bool
*/
function arrays_are_similar(array $a, array $b) {
if (count(array_diff_assoc($a, $b))) {
return false;
}
foreach($a as $k => $v) {
if ($v !== $b[$k]) {
return false;
}
}
return true;
}
}