Test methods of Abstract Class with PHPUnit - phpunit

I have an abstract class that has common methods in it, that I wish to test, so I do not have to keep testing them in each class that extends this class.
abstract class Class1 implements iClass1
{
const VALUE = 'A';
private $Return;
public function __construct($Field = NULL)
{
if( ! is_null($Field) )
$this->SetField($Field);
}
public function GetField()
{
return $this->Return;
}
public function SetField($Field)
{
if (strlen($Field) != 3)
throw new CLASS1_EXCEPTION('Field "' . $Field . '" must be 3 digits.');
$this->Return = $FieldCode;
}
abstract function CalculateData();
}
I want to create the basic test case then that will test the constructor, and the GetField and other functions, then my other test files can test the abstract functions.
I want to be able to test the const has not changed, the field throws the exception etc...
TEST:
class TEST_CLASS1 extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
require_once('CLASS1.php');
}
public function testConstants()
{
$this->assertEquals(CLASS1, 'A');
}
/* #expectedException CLASS1_EXCEPTION
public function testLargeFieldException()
{
$class1 = new CLASS1('ABCD');
$class1 = new CLASS1();
$class1->SetField('ABCD');
}
}
How do I create the tests since I can not create the CLASS1 object as it is an abstract class?

One option is to create a
TestableClass1 extends Class1 {
public function CalculateData() {}
}
and use that class for your tests.
The other option is to do pretty much the same but use an API phpunit provides you with:
For this see the sample Example 10.13: Testing the concrete methods of an abstract class of the phpunit documentation:
A simpler example:
abstract class AbstractClass
{
public function concreteMethod()
{
return 5;
}
public abstract function abstractMethod();
}
class AbstractClassTest extends PHPUnit_Framework_TestCase
{
public function testConcreteMethod()
{
$sut = $this->getMockForAbstractClass('AbstractClass');
$this->assertSame(5, $sut->concreteMethod());
}
}

Related

Symfony 3.4 apply tag on all classes who implement same interface

I'm working with a Symfony 3.4 (PHP 7.2, update to 7.4 soon) project. I have some classes who extends an abstract class and i would like all my classes got the same constant name (constant have value different in each class). I'm starting with a pattern like this :
abstract class AbstractClass
{
abstract public function getConstant(): string;
}
final class Foo extends AbstractClass
{
const MY_CONST = 'foo';
public function getConstant(): string
{
return self::MY_CONST;
}
}
final class Bar extends AbstractClass
{
const MY_CONST = 'bar';
public function getConstant(): string
{
return self::MY_CONST;
}
}
// echo $foo->getConstant() : 'foo'
// echo $bar->getConstant() : 'bar'
The goal: if a class who extends AbstractClass don't have MY_CONST, i want return an message error.
I have excluded theses solutions :
I can't add a constant in an interface (maybe in PHP 8 ?)
I can't use "abstract factory" pattern for a constant (in my code it runs with getConstant() method
I can't use a static property
The only way i have found is : implement an interface and tag the interface like explain in documentation. With compilerpass, helped with ReflexionClass, i will check if constant name exist, and if not: thrown an error or something like this.
So, i've edit like this :
final class Foo extends AbstractClass implements MyCustomInterface
{
// ...
}
final class Bar extends AbstractClass implements MyCustomInterface
{
// ...
}
The interface :
interface MyCustomInterface
{
}
Adding tag in AppKernel.php
protected function build(ContainerBuilder $container): void
{
$container
->registerForAutoconfiguration(MyCustomInterface::class)
->addTag('my_custom_tag');
}
And a compilerpass :
class MyCustomPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->has(MyCustomInterface::class)) {
dump('No interface found');
return;
}
$definition = $container->findDefinition(MyCustomInterface::class);
$taggedServices = $container->findTaggedServiceIds('my_custom_tag');
dump($taggedServices);
}
}
The fun begin here...if i have only ONE class who implement the interface, $taggedServices find the service. BUT, if more than one class implements interface, no class are found...
I don't find where i am wrong. Do i need to implements AbstractClass instead of children classes ?

Why do actions with class and public method don't fire __construct()

I'm trying to understand how WordPress works with actions, classes, and methods.
If there is a class "TestClass" and it has a public method 'method1'
The method can be hooked to any action as "add_action('theHook', ['TestClass', 'method1']);"
From my understanding. If you don't initialize the class, you can not access its public methods and objects. Now, I would assume that WordPress has to follow this, and it must initialize my "TestClass", which will cause for public __construct() to fire.
However, after testing this, it does not fire __construct()..
Why is this?. I know a fix would be to self initialize inside 'method1', but I'm trying to figure out why WordPress behaves this way.
Because WordPress call your method as a static function: TestClass::method()
There is various solution:
1. Init class before add Action
Initialize your class before add action, like that:
$test = new TestClass();
add_action('hook', [$test, 'method']);
2. Call hook inside your Class:
class TestClass {
public function __construct() {
// Your construct
}
public function method() {
// Your Method
}
public function call_hook() {
add_action('hook', [$this, 'method']);
}
}
$test = new TestClass();
$test->call_hook();
3. Use a singleton
And if you need to to have only one instance of your class and call it in various place, you have to take a look to Singleton design pattern.
Demonstration:
class MySingletonClass {
private static $__instance = null;
private $count = 0;
private function __construct() {
// construct
}
public static function getInstance() {
if (is_null(self::$__instance)) {
self::$__instance = new MySingletonClass();
}
return self::$__instance;
}
public function method() {
$this->count += 1;
error_log("count:".$this->count);
}
}
$singleton = MySingletonClass::getInstance();
add_action('wp_head', [$singleton, 'method']);
$singleton2 = MySingletonClass::getInstance();
add_action('wp_footer', [$singleton2, 'method']);

PHPUnit: Method in mocked abstract class returns null

I'm writing tests against the following class:
abstract class EmailMessageRecipient
{
private $_address = null;
public function setAddress($address)
{
$this->_address = $address;
return $this;
}
public function getAddress()
{
return $this->_address;
}
}
The test looks like this:
class EmailMessageRecipientTest extends PHPUnit_Framework_TestCase
{
private $_test_object;
protected function makeTestObject()
{
return $this->createMock(EmailMessageRecipient::class);
}
public function setUp()
{
$this->_test_object = $this->makeTestObject();
}
public function testAddress()
{
$this->_test_object->setAddress('blah#example.com');
$this->assertEquals('blah#example.com', $this->_test_object->getAddress());
}
}
PHPUnit fails the test with the message Failed asserting that null matches expected 'blah#example.com'.. So $this->_test_object->getAddress() is returning null instead of the email address that was passed to it. Why is this happening and how do I adjust the test so that it passes.
Mocking the setAddress() and getAddress() methods is not an acceptable answer. The purpose of this test is to cover setAddress() and getAddress().
If you test an abstract class you should use: getMockForAbstractClass:
The getMockForAbstractClass() method returns a mock object for an
abstract class. All abstract methods of the given abstract class are
mocked. This allows for testing the concrete methods of an abstract
class.
Example 9.19: Testing the concrete methods of an abstract class
<?php
use PHPUnit\Framework\TestCase;
abstract class AbstractClass
{
public function concreteMethod()
{
return $this->abstractMethod();
}
public abstract function abstractMethod();
}
class AbstractClassTest extends TestCase
{
public function testConcreteMethod()
{
$stub = $this->getMockForAbstractClass(AbstractClass::class);
$stub->expects($this->any())
->method('abstractMethod')
->will($this->returnValue(true));
$this->assertTrue($stub->concreteMethod());
}
}
?>

How to define javafx callback with no prarameter

I don't know how to define a callback that doesn't need any parameter. In order to describe the question, let me make up a setup - mine is different but contains several more classes, harder to digest.
Lets say we have
public class CustomerA {
public Payment payWithCash() {...}
}
and
public class CustomerB {
public Payment payWithCreditCard() {...}
}
and in addition
public abstract class Factory {
public Callback<Void, Payment> getPaymentCallback();
// Some other methods
...
}
then here comes the problem: I'd like to implement something like this
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return CustomerA::payWithCash;
}
}
and in a different class
public class CashFactoryB extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return CustomerB::payWithCreditCard;
}
}
What happens is, that the compiler complains, that CustomerB does not define payWithCreditCard(Void) and CustomerA likewise fails for payWithCash(Void).
So how to state this correct that there is no parameter to the Callback?
I am aware that I could probably solve my problem as well with interfaces, but I like to understand how to solve this with a Callback.
Thank you in advance!
Consider making Factory generic:
public abstract class Factory<T> {
public Callback<T, Payment> getPaymentCallback();
// Some other methods
...
}
And then you can do
public class CashFactoryA extends Factory<CustomerA> {
public Callback<CustomerA, Payment> getPaymentCallback() {
return CustomerA::payWithCash;
}
}
and
public class CashFactoryB extends Factory<CustomerB> {
public Callback<CustomerB, Payment> getPaymentCallback() {
return CustomerB::payWithCreditCard;
}
}
EDIT:
This answer is a wrong approach.
First, you are trying to refer payWithCash from not a instance of CustomerA but the class CustomerA, so payWithCash must be static method.
public class CustomerA {
public static Payment payWithCash() {...}
}
Then use lambda expression which is the same interface as your Callback.
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return param -> CustomerA.payWithCash();
}
}
The code above has the same meaning as the code below. The param argument will be ignored as a result.
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return new Callback<Void, Payment>(){
#Override
public Payment call(Void param) {
return CustomerA.payWithCash();
}
};
}
}

Mocking Illuminate\Database\Eloquent\Model

I need to mock Laravel's Eloquent\Model with Mockery and it is kind of tricky because it uses static methods.
I solved this issue with the following code but I wonder if there is a better/smarter way to do this.
<?php
use Ekrembk\Repositories\EloquentPostRepository;
class EloquentPostRepositoryTest extends TestCase {
public function __construct()
{
$this->mockEloquent = Mockery::mock('alias:Ekrembk\Post');
}
public function tearDown()
{
Mockery::close();
}
public function testTumuMethoduEloquenttenAldigiCollectioniDonduruyor()
{
$eloquentReturn = 'fake return';
$this->mockEloquent->shouldReceive('all')
->once()
->andReturn($eloquentDongu);
$repo = new EloquentPostRepository($this->mockEloquent);
$allPosts = $repo->all();
$this->assertEquals($eloquentReturn, $allPosts);
}
}
Tough to tell without the source of "Ekrembk\Repositories\EloquentPostRepository", but, I see a couple of issues. It looks like within your EloquentPostRepository, you're calling a static. You shouldn't do that. It makes it hard to test (as you've discovered). Assuming that Ekrembk\Post extends from Eloquent, you can do this instead:
<?php
namespace Ekrembk\Repositories
class EloquentPostRepository {
protected $model;
public __construct(\Ekrembk\Post $model) {
$this->model = $model;
}
public function all()
{
$query = $this->model->newQuery();
$results = $query->get();
return $results;
}
}
Then, your test will be simpler:
<?php
use Ekrembk\Repositories\EloquentPostRepository;
class EloquentPostRepositoryTest extends TestCase {
public function tearDown()
{
Mockery::close();
}
public function testTumuMethoduEloquenttenAldigiCollectioniDonduruyor()
{
$mockModel = Mockery::mock('\Ekrembk\Post');
$repo = new EloquentPostRepository($mockModel);
$eloquentReturn = 'fake return';
$mockModel->shouldReceive('newQuery')->once()->andReturn($mockQuery = m::mock(' \Illuminate\Database\Eloquent\Builder'));
$result = $mockQuery->shouldReceive('get')->once()->andReeturn($eloquentReturn);
$this->assertEquals($eloquentReturn, $result);
}
}
Didn't test the above so it might have issues, but you should get the idea.
If you look in Illuminate\Database\Eloquent\Model, you'll see that "public static function all" is really just calling "get" on an instantiated eloquent model.

Resources