I created one class (ParseDAO.php) and made it singleton with this method:
public static function getInstance(){
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
Then I created 2 controllers. One to login stuff (LoginController.php) and another do dashboard stuff (DashboardController.php)
In LoginController.php I use this code and it works perfectly:
$instance = ParseDAO::getInstance();
$loginResponse = $instance->loginParse($request->get('userName'), $request->get('password'));
if($loginResponse == true){
return redirect()->route('dashboard');
}
else {
return view('login.erroLogin');
}
At DashboardController I have this code:
$instance = ParseDAO::getInstance();
$userId = $instance->getUserId();
This second line just returns the objectId form parse. If I put this line on loginController.php it returns the correct Id, but at DashboardController.php (where I need this data) nothing returns.
It's like another instance was created even using singleton.
Anybody knows how to solve this problem?
Follow the code of loginParse and getUserId:
/**
* Method login
*/
public function loginParse($username, $password){
if($username != null && $password != null){
try {
self::$user = ParseUser::logIn($username, $password);
return true;
}catch (ParseException $error){
return false;
}
}
else{
return false;
}
}
/**
* #return getUserId
*/
public function getUserId(){
return self::$user->getObjectId();
}
And this is the constructor code (with the true keys):
public function __construct()
{
ParseClient::initialize('xxx','xxx','xxx');
self::$user = new ParseUser();
}
Singletons allow for code from a single execution to use the same instance. On every execution (page load), a new instance is created. Every call to ParseDAO::getInstance() will return the same instance, but only within that execution context.
Without seeing the code in loginParse and getUserId, what is most likely occurring is that you are storing information in the ParseDAO class. That information would be kept during the same execution context, but would disappear on the next execution (because it's a new instance).
You'll have to use Sessions to persist information across multiple executions.
Laravel's Service Container offers a quick and easy way to bind a class as a singleton without the need to implement that pattern yourself. So you can use this to register you singleton with the service container:
app()->bind('ParseDAO', function ($app) {
return new ParseDAO;
});
And then you can use this to access that instance:
$instance = app('ParseDAO');
That will make sure you always get the same instance. You can read more about singletons and the Laravel Service container in the Laravel Documentation.
Related
I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.
I have a save function for create new document with mongoDB
public function save(User $user): User
{
$result = $this->usersCollection->insertOne($user->getUser());
$user->setId($result->getInsertedId());
return $user;
}
And Change __construct for implemet test
public function __construct($db = null)
{
if (is_null($db)) {
parent::__construct();
} else {
$this->db = $db;
}
$this->usersCollection = $this->db->users;
}
I write this test for save function
public function testSave()
{
$mongo = \Mockery::mock('MongoDB\Client');
$mongo->shouldReceive('insertOne')->andReturn("ok");
$mongo->shouldReceive('selectDatabase')->andReturnSelf();
$user = new User('jack', '0015005050');
$um = new UserMongoDB($mongo);
$res = $um->save($user);
}
everything works well but my problem is $result->getInsertedId() How to I can Mock this function?
Error : Call to a member function getInsertedId() on string
The return type of the insertOne method must be an instance of InsertOneResult (see docs). At the moment you are returning the string "ok". You could keep going and make insertOne return a mock of InsertOneResult. This may work but you are at the gate to mocking hell. Personally, I'd write integration tests for the save method. Mocking the save method in other unit tests is way easier than mocking the low level MongoDB stuff all over the place.
I have the following class for all my user methods:
class User {
protected $_db,
$_data;
public function __construct($user = null, $findby = 'id') {
$this->_db = DB::getInstance();
if (!$user) {
........
} else {
........
}
}
.......
public function login($username = null, $password = null) {
$user = $this->find($username, 'username');
if ($user) {
$lockdown = new Lockdown;
}
}
public function find($param = null, $method = null) {
if ($param && $method) {
$data = $this->_db->query("SELECT * FROM users ...");
if ($data->count()) {
$this->_data = $data->result();
return true;
}
}
return false;
}
public function data() {
return $this->_data;
}
}
The above is a completely stripped down version of my user class. I also have another class (lockdown) which extends user:
class Lockdown extends User {
public $getAttempts;
public function __construct() {
var_dump($this->data());
die();
}
}
However when i call the lockdown class inside of the login class, even though the data object should contain all the user information, the var_dump() is simply returning NULL.
From my calculations when the login class is called, the find method should set $_data = USER INFO, which should therefore allow the new Lockdown method invoked just after the ($this->find()) to be able to access the same data method.
I am still learning OOP Programming so don't know if there is something i am missing, but i can't seem to understand the reason as to why the Lockdown class returns NULL on the data method when it should inherit it.
You should not put any computation logic inside a constructor. It makes it hard to test. You also cannot return from a constructor.
You structure is a complete disaster. Both because of your abuse of the inheritance and of global state.
It makes no sense for a class to create a new instance of its own child class for retrieving data. This is probably a result of you attempting to for a User class to combine two different responsibilities: persistence and business logic. This constitutes a violation of Single Responsibility Principle, which then manifest in a form of a convoluted call graph.
Also the whole class Lockdown extends User construct makes no sense. The extends keyword in OOP can be translates as "is special case of" (as per LSP). The class for tracing user's login attempts is not a specialized case of "user".
You should have at least 3 separate classes for this: one for handling the "user's behavior" and other for saving/restoring "user's state" (the approach is called "data mapper"). The third one would be for managing the the failed attempts.
I would also highly recommend watching this lecture.
As for global state, instead of using a singleton anti-pattern, you should have passed the database connection as a constructor's dependency to the class, which need to interact with persistence.
As for the code, at a high level, it should probably looks something like this:
$user = new User;
$mapper = new UserMapper($db);
$user->setName($username)
if ($mapper->fetch($user)) {
if ($user->matchPassword($password)) {
// you have logged in
// add some flag in session about it
header('Location: /greetings');
exit;
}
// check the failed attempts
} else {
// no matching username
}
I'm grappling with mocking/Mockery for the first time and I'm unsure if the following test is actually touching my code, or is only testing the mock I've made? Also, I realize this code doesn't properly fit the repository pattern despite the fact it's name as such.. I'll work on that.
The class:
<?php namespace Acme\Cart\Repositories;
class EloquentCartRepository{
protected $model_name = 'CartModel';
protected $model;
public function __construct($model = null)
{
$this->model = is_null($model) ? new $this->model_name : $model;
}
public function create_visitor_cart($session_id,$type = 'main'){
return $this->create('visitor',$session_id,$type);
}
protected function create($user_type = null,$user_identifier = null,$type = 'main')
{
if(is_null($user_identifier)) throw new \Exception('Cannot create create cart, missing user identifier');
if(is_null($user_type)) throw new \Exception('Cannot create create cart, missing user type');
if($user_type == 'visitor')
{
$this->model->user_session_id = $user_identifier;
}
else
{
$this->model->user_id = $user_identifier;
}
$this->model->type = $type;
$this->model->save();
return $this->model;
}
}
And my test:
/** #test */
public function create_visitor_cart_calls_internal()
{
$model = m::mock('Models\CartModel');
$model->shouldReceive('user_session_id')->with('sess123');
$model->shouldReceive('type')->with('main');
$model->shouldReceive('save')->andReturn($model);
$repository = new EloquentCartRepository($model);
$created_model = $repository->create_visitor_cart('sess123','main');
$this->assertEquals('sess123',$created_model->user_session_id);
$this->assertEquals('main',$created_model->type);
}
Is this a proper way to test my class? Or is this incorrect use of Mockery/mocking?
Instead of testing what is returned, you should test that it is saved. That means, that ->save() is run. The expectation you've set on ->save() is $model->shouldReceive('save')->andReturn($model);. That doesn't make sense, since the code doesn't use the return value of ->save().
In programming, you usually deal with 2 types of method: Commands and Queries. Queries can get some value, do some logic and return a value. Commands can get some values, communicates with an extern source (e.g. a database) and return nothing. Queries should be stubbed (that means, they should not do any expectations on how much it is called, but only on what it returns) and commands should be mocked (that means, they should only contain expectatations on how much (and if) it is called).
The ->save() method is a command: It communicates with the database. So it should be mocked. To mock the object, use the ->once() method of Mockery. It sets an expectation that it should be called one time:
/** #test */
public function create_visitor_cart_calls_internal()
{
$model = m::mock('Models\CartModel');
$model->shouldReceive('save')->once();
$repository = new EloquentCartRepository($model);
$created_model = $repository->create_visitor_cart('sess123','main');
$this->assertEquals('sess123',$created_model->user_session_id);
$this->assertEquals('main',$created_model->type);
}
Despite its name, Mockery is a stubbing framework by default. It does not validate that a method is called unless you explicitely specify an expectation like ->once()
For more information, see the docs: https://github.com/padraic/mockery-docs/blob/master/reference/expectations.rst
I'm trying to build a form wizard in Kohana and am learning a bit as I go. One of the things that I've learn might work best is utilizing a state pattern in my class structure to manage the different steps a user can be in during the form process.
After doing some research, I've been thinking that the best approach may be to use an interface and have all of the steps act as states that implement the interface. After a state validates, it will change a session variable to the next step, which can be read upon the initial load of the interface and call the correct state to use.
Does this approach make sense? If so, how the heck do I make it happen (how do I best structure the filesystem?)
Here is the rough start I've been working on:
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Project_Builder #state
* Step_One #state
* Step_Two #state
**/
interface Project_Builder
{
public function do_this_first();
public function validate();
public function do_this_after();
}
class Step_One implements Project_Builder {
public function __construct
{
parent::__construct();
// Do validation and set a partial variable if valid
}
public function do_this_first()
{
echo 'First thing done';
// This should be used to set the session step variable, validate and add project data, and return the new view body.
$session->set('step', '2');
}
public function do_this_after()
{
throw new LogicException('Have to do the other thing first!');
}
}
class Step_Two implements Project_Builder {
public function do_this_first()
{
throw new LogicException('Already did this first!');
}
public function do_this_after()
{
echo 'Did this after the first!';
return $this;
}
}
class Project implements Project_Builder {
protected $state;
protected $user_step;
protected $project_data
public function __construct()
{
// Check the SESSION for a "step" entry. If it does not find one, it creates it, and sets it to "1".
$session = Session::instance('database');
if ( ! $session->get('step'))
{
$session->set('step', '1');
}
// Get the step that was requested by the client.
$this->user_step = $this->request->param('param1');
// Validate that the step is authorized by the session.
if ($session->get('step') !== $this->user_step)
{
throw new HTTP_Exception_404('You cannot skip a step!');
}
// Check if there is user data posted, and if so, clean it.
if (HTTP_Request::POST == $this->request->method())
{
foreach ($this->request->post() as $name => $value)
{
$this->project_data["$name"] = HTML::chars($value);
}
}
// Trigger the proper state to use based on the authorized session step (should I do this?)
$this->state = new Step_One;
}
public function doThisFirst()
{
$this->state = $this->state->do_this_first();
}
public function doThisAfter()
{
$this->state = $this->state->do_this_after();
}
}
$project = new Project;
try
{
$project->do_this_after(); //throws exception
}
catch(LogicException $e)
{
echo $e->getMessage();
}
$project = new Project;
$project->do_this_first();
$project->validate();
$project->do_this_after();
//$project->update();
Your way certainly looks possible, however I would be tempted to keep it simpler and use some of Kohanas build in features to take care of what you want. For example, I would use Kostache (mustache) and have separate View classes (and potentially templates) for each step. Then the controller becomes quite simple. See the example below (missing session stuff and validation of the step_number). All of the validation is handled in the model. If there is a validation error, an exception can be thrown which can then pass error messages back to the View.
<?php
class Wizard_Controller {
function action_step($step_number = 1)
{
$view = new View_Step('step_' + $step_number);
if ($_POST)
{
try
{
$model = new Model_Steps;
$model->step_number = $step_number;
if ($model->save($_POST))
{
// Go to the next step
$step_number++;
Request::current()->redirect('wizard/step/'.$step_number);
}
}
catch (Some_Kind_Of_Exception $e)
{
$view->post = $_POST;
$view->errors = $e->errors();
}
}
$view->render();
}
}
?>
Hope this makes sense.