I want to mock a static method which has been used in another method using Mokcery,Just as follows:
Class SomeClass
{
public static function methodA()
{
.....;
self::B();
}
public static function methodB()
{
Do SomeThing
}
}
if I want to mock methodB,and use methodA,the mock function doesn't work, just because methodB is used in methodA,just as below
use Mockery as m;
$mocktest = m::mock->('SomeClass[B]');
$mocktest->shouldReceive('B')->andReturn("expectedResult");
$mocktest->methodA();
The code above will result in methodB still return it's original result rather than 'expectedResult'.
I expect the methodB used in the methodA to be mocked,how could I operate?
You need to use an alias to mock a static method:
$mock = \Mockery::mock('alias:SomeClass');
Note that class can't be loaded yet. Otherwise mockery won't be able to alias it.
More in the docs:
Mocking Public Static Methods
Quick Reference
Just be warned that mocking static methods is not a good idea. If you feel like you need it you have problem with design.
Mocking the class you're testing is even worse and indicates your class has too many responsibilities.
Related
Given I have a FruitSalad class (the system under test):
class FruitSalad
{
protected $fruits = [];
public function addFruit(Fruit $fruit)
{
$this->fruits[] = $fruit;
return $this;
}
}
And I have a Fruit class:
class Fruit
{
public static function withName($name)
{
$instance = new MyDependencyClass();
$instance->name = $name;
return $instance;
}
}
A trivial example, however you can see that the Fruit class uses a named static constructor, and the addFruit() method on the FruitSalad class type hints Fruit as its expected parameter.
When writing a test for addFruit(), I need to mock the Fruit class.
function test_it_can_add_a_fruit_to_its_list_of_fruits()
{
$fruit = $this->getMockBuilder('Fruit')
->disableOriginalConstructor()
->getMock();
$this->fruitSalad->addFruit($fruit);
// Do some assertion.
}
This creates a simple mock of the Fruit class, but I want to instantiate it via the withName() static method - and I do not want to expose a setter for the name property.
How can I create a mock for Fruit using the static named constructor?
PHPUnit used to support mocking static methods, but since PHPUnit 4.0 it's omitted. I see four options here:
1. Don't mock the method at all
You could just call the method and use it's logic, although you'd test the static method as well if you do and normally that's something you should avoid when writing unit tests.
2. Change the class
Ask yourself if this method really needs to be static and if not, change the class to test it properly. There are quite some use cases where it's better to change some of your architecture in order to write proper tests.
3. Use a spy class
Spy classes are classes that extend a class that you would usually mock, but implement some logic for testing the configuration of a class or the dependency of a tested method to another method. In the very most cases this can be avoided by mocking the class properly. Spies are simply your work around if mocks are not enough, there are very few cases in which you really need them.
However, in this case a spy could be used to overwrite a static method as a work around:
class FruitSpy extends Fruit
{
public static $return;
public static $name;
public static function withName($name) {
$expected = self::$name;
if($name == $expected) {
return self::$return;
} else {
throw new \RuntimeException("FruitSpy::withName(): Parameter 0 was $name, $expected expected");
}
}
}
This example checks for the correct $name and, if it's correct, returns your defined return. You'd use it like this in your test:
$fruitSpy = new FruitSpy();
$fruitSpy::$name = "Banana";
$fruitSpy::$return = new \stdClass();
$this->fruitSalad->addFruit($fruitSpy);
Not exactly a clean solution, but the only way I see if you absolutely positively don't want to change other code than the test code.
Again, you should think about changing the static method to a casual method if you need to do something like this.
4. Use PHPUni 3.*
You could simple use a deprecated version of PHPUnit to use this method. Not a preferred way either.
Conclusion
I don't see a clean way to mock a static method and ::staticExpects() was removed for a reason in 4.0
How can I create a mock for Fruit using the static named constructor?
You can't. Mocks are created by using a mocking framework.
Anyway it does not matter how mocks are created but, instead, how they behave, because they're external to the class being tested.
Just configure the mock so that it behaves the same way a real Fruit instance would when created using Fruit::withName.
Assuming that I have to create a class that takes some text do some processing and return it ... with no dependency and it's a stateless class..
I'd like to know would be better to create a stateless class without constructor or just create a static class (in php it's just Static methods)
class like this:
class ClassName
{
public function processText($text)
{
// Some code
$text = $this->moreProcessing($text);
return $text;
}
protected function moreProcessing($text)
{
return $text;
}
}
and this:
class ClassName
{
public static function processText($text)
{
// Some code
$text = static::moreProcessing($text);
return $text;
}
protected static function moreProcessing($text)
{
return $text;
}
}
I Know that dependency injection into the class where these classes are used would be better but assume that I just won't have dependency injection..
My question is mainly would it be better to create static class for the simple example above?
Practically you will see no difference whatsoever.
It's only in the syntax, and the ability of a constructor to perform stuff automatically, though you still have to create instances to invoke the constructor, which in this case is not far off calling some equivalent static member function.
However, non-static member functions are supposed to affect internal state so, if you have no state, static member functions seem more conventional, and will be slightly less surprising to users of the class.
The best approach, though, is to stick your functions in a namespace. Classes are for data and functions operating on that data... even static ones.
Why are static methods untestable? Kindly exemplify (in PHP if possible).
Static methods themselves aren't untestable, but if the object being tested calls a static method then a test cannot "get in between" and make it call a stub method instead. If the object being tested instead calls a regular method, the test can give it an alternative object with a stub implementation of that method.
In general, rigid dependencies are less testable while dependency injection (google it) makes code more testable.
For instance, let's say we have a static method getCurrentUser() that is used by the class we are testing, as follows
class PostModel {
//...
public function getRecentPosts() {
return $this->database->from('posts')
->where(array('user' => UserModel::getCurrentUser()))
->limit(10);
}
}
Now UserModel::getCurrentUser() cannot be replaced with a stub method. If we make it a regular method that we call through an object reference instead, we can pass in an alternative stub object in our test.
class PostModel {
private $userModel;
public function __construct($userModel) {
$this->userModel = $userModel;
}
//...
public function getRecentPosts() {
return $this->database->from('posts')
->where(array('user' => $this->userModel->getCurrentUser()))
->limit(10);
}
}
Static methods are testable: http://sebastian-bergmann.de/archives/883-Stubbing-and-Mocking-Static-Methods.html (it's in php), but if you want to test the interactactions between classes (i.e. using mocks and fake objects) you will probably prefer not to use it. That being said phpunit 3.5 allows you to stub these.
You might also want to look at When do I use static variables/functions in php? or search for some information about when to use static methods.
I use static methods inside a class (ie marked private or protected) as they are faster in the language I work with.
Well-defined Static methods are perfectly testable. You see, the problem isn't with the method itself, it's with the method's dependencies. If the method and its dependencies (and the dependencies' dependencies) are idempotent, then there's no problem. Problems arise when your Static methods are calling other methods that are, for example, dependant on a global.
Here is a simple example:
class Class_A {
protected $_property;
public function method()
{
Class_B::method($this);
}
public function getProperty()
{
return $this->_property;
}
}
class Class_B {
public static function method(Class_A $classA)
{
$classA->getProperty();
}
}
$classA = new ClassA();
$classA->method();
Is it ever okay to pass $this as a parameter to the method of another class? Or is that always going to be tight coupling? I could pose another similar example using a Factory Pattern in place of the static method call.
It depends on the exact behaviour of Class_A and Class_B, but in general it would probably be better to define an interface which is implemented by Class_A and type hint for that. The methods of Class_A that are required by Class_B (e.g. getProperty()) should appear in your interface. Then, if you want to switch Class_A with another class at a later date, all it has to do is implement the same interface.
Yet again, it depends on the behavior of the classes in question, but if there was another Class_C for example that also used Class_B 's static method you might want to consider having Class_A and Class_C extend Class_B. More information can be found on the php object inheritance page.
The code to be tested
abstract class Parent
{
public function getSomething(){} //this has to be mocked
}
class Child extends Parent
{
public function methodWhichIsTested()
{
$something = $this->getSomething(); //call to parent method
}
}
The test
public function setUp()
{
$this->child = new Child;
}
public function testTheChildMethod()
{
$this->child->methodWhichIsTested();
}
How can I add mock expectations to the instantiated class Child?
I would like to do something like:
MockFramework->takeExistingClass('Child')->shouldRecieve('getSomething')->andReturn('whatever');
My problem is, that in the real case (not the example), the getSomething method returns a dependency, which I need to mock!
I am using Mockery but if you know how to do this with phpUnit mocks, go ahead! Maybe I'm making a basic thinking mistake, please give me a hand! Thanks.
After you clarfied in chat that the returned value of getSomething holds a dependency that is
a protected property of the abstract class, and it is injected into that abstract via another public method of the abstract
the solution is inject a mock of that dependency via that other method.
In general, you should never have the need to mock or stub behavior of the TestSubject. It is only when you are making lookups to the Global Scope or mix/hard code object creation into the TestSubject, that you might see the need for that, but these would be code smells and should be refactored instead. See Sebastian Bergmann's articles on untestable code:
Testing private methods
Testing code that uses singletons
Stubbing static methods
Stubbing hard-coded dependencies