Need a bit of help using Mockery - I want to overload a class, which is created using new HelperUtil() in a method.
Using Mockery overload I can do this, but it is leaving me with an empty shell class. Which I then appear to have to create all methods that are called. Is there a way to create a overloaded full mock, and then change just one method?
$mock = \Mockery::mock('overload:'.HelperUtil::class);
$mock->shouldReceive('content')->andReturnUsing(function() {
return 'different content';
});
thanks
edit:
I think i want to do:
$mock = \Mockery::mock('overload:'.HelperUtil::class)->shouldDeferMissing();
$mock->shouldReceive('content')->andReturnUsing(function() {
return 'different content';
});
But that still dosnt work =(
There is an interesting discussion about it on GitHub
shouldIgnoreMissing and shouldDeferMissing (and other such flags
present and future) do work, but not on the mock, but on the instance
mock.
Sounds confusing, but it's because of internals.
When you do a m::mock('overload:Foo'); mockery goes and creates a new
class called Foo based on the Mock.php "template". And that template
has the _mockery_ignoreMissing flag set to false by default.
Once the mock method gets to the end, it creates an overloaded Foo
object and returns it to us. The overloaded Foo class is also
available to instantiate new Foo objects.
Calling $m->shouldIgnoreMissing() sets the ignore missing flag on the
object that was returned by the mock method.
Calling new Foo() creates a new instance of the Foo object that has
the Mock.php templates default of false for _mockery_ignoreMissing.
If you want the flag set to true on the new instance, you need to call
shouldIgnoreMissing() on that instance, and not on $m.
As I see, feature "should ignore missing on instance mocks" has been completed, so the best you can do is
$mock = \Mockery::mock('overload:'.HelperUtil::class)->shouldIgnoreMissing();
I don't see shouldDeferMissing has been done yet for overload.
Of course, as a workaround, you might consider creating a mock and injecting it.
Related
I need to mock CurrencyEnum by overload it, but it's not the end becouse i need to add interface to this mock.
This doesn't work:
Mockery::mock('overload:'.CurrencyEnum::class);
Error: (..) must be an instance of \BaseCurrency, instance of \CurrencyEnum given.
I looked at Mockery\Container::mock and I dont't have idea how to do it.
In example I want to test TestingClass::first() method
class CurrencyEnum implements BaseCurrency
{
/* methods */
}
class TestingClass
{
public function first(string $currencySymbol)
{
$abc = 'some_string';
return $this->second($abc, new CurrencyEnum($currencySymbol));
}
private function second(string $abc, BaseCurrency $currency)
{
/* code */
}
}
The overload method works by intercepting the autoload mechanism: it registers an autoloader for the overloaded class, loading the mocked version of the class instead of the original.
By default, it does not add many things to the mocked class. You can, however, configure just about anything you may need.
Usually, implementing one or more interfaces can be done by providing a comma-separated list of fully qualified names, the first one being the class:
$mock = Mockery::mock('MyClass, MyInterface, OtherInterface');
Due to the way that the Mockery::mock method is set up, this will not work. (The author apologises in the source code)
However, we can pass the interface(s) as second argument to the mock method:
Mockery::mock('overload:'.CurrencyEnum::class, BaseCurrency::class);
This will cause the MockConfigurationBuilder to add BaseCurrency as target; since it's an interface it will make the mock implement the interface.
An alternative notation of the above would be to use the builder directly:
Mockery::mock(
(new MockConfigurationBuilder())
->setInstanceMock(true)
->setName(CurrencyEnum::class)
->addTarget('stdClass')
->addTarget(BaseCurrency::class)
)
Having said that, it's a notoriously bad practice to mock things like enums and value objects. Why not just use the actual CurrencyEnum? Something as simple as a currency code does not quite warrant mocking at all. There's probably a structural improvement to make, which would simultaneously add tons of value to your tests and make them simpler to read.
Write tests to coverage 100% code is something we should attempt to achieve. But I came up with situaction where I don't know how to test method (factory method):
public function getDocument(){
$document = new Document();
$document->settings(new Settings());
$document->filesystem(new Filesystem('e:'));
return $document;
}
Purpose of that method is shortcut to create document, without everytime write 3 lines.
How to test this method?
Or maybe this is situation why we have #codeCoverageIgnoreStart block? Exactly for that reason PHPUnit provide this kind fo annotations.
EDIT:
The main idea behind this method is make client life easier. Nothing more, no configuration etc.(but the method will be good place to do it).
//I don't want bother client with Settings() and Filesystem('e:')
$document = new Document(new Settings(), new Filesystem()); //NO
$document = Files.getDocument() //much easier and shorter.
//Changing API to getDocument($var, $var) make no sense, the same thing I could have normally.
$document = new Document(new Settings(),new Filesystem('e:'));
Maybe I should thing about if I really should provide that method, user who want use document should know of dependences, it shouldn't be hide.
What this method does? Returns initialized Document object. So all you have to verify is that the returned value is a Document instance and that it has Settings and Filesystem objects set. Easy if you have getters for those, otherwise you have to access the respective properties.
The test may sound very basic, but it does test what it needs to. When you refactor your code in a way that the settings and filesystem are injected, the test will still tell you if the document has those properties set at all.
It's called unit testing because you are testing a unit, not an object or a method. If your unit has multiple classes, let it be. There's no need everything to be injected and there's no need everything to be mocked - those things ease testing, but in certain cases it's even better not to mock them
Inject your dependencies (Document, Settings, Filesystem) via the constructor, then use test doubles as appropriate.
Also reconsider your 100% coverage policy, it's definitely not clear that it's actually a good thing.
Pass the dependencies to the factory method, initialize the new object inside, and configure it properly. In the test, the dependencies will be mocks instead of real objects.
method 1
Pass factories that allow to create the dependencies:
public function getDocument(SettingsFactory $sf, FilesystemFactory $ff){
$document = new Document();
$document->settings($sf->getSettings());
$document->filesystem($ff->getFilesystem());
return $document;
}
In the test, you should:
create Settings instance or mock and a SettingsFactory mock that expects one call to getSettings and will return the Settings instance
create Filesystem instance or mock and a FilesytemFactory mock that expects one call to getFilesystem and will return the Filesystem instance
call the DocumentFactory method, passing the factories. Check that a Document object is returned
check that the objects assigned to Document are the same that you configured the mocks to return
A variant on this is having the getSettings and getFilesystem as methods of the Document factory. In that case you should create a partial mock of the Factory, and set the expectations on it. So the real getDocument method is called, but when getSettings and getFilesystem methods are called, you return controlled instances.
method 2
Pass the actual dependencies:
public function getDocument(Settings $settings, Filesystem $filesystem) {
$document = new Document();
$document->settings($settings);
$document->filesystem($filesystem);
return $document;
}
In the test, you should:
create Settings instance or mock
create Filesystem instance or mock
call the DocumentFactory method, passing the Settings and Filesystem. Check that a Document object is returned
check that the objects assigned to Document are the same instances that you passed to the factory method
Kind of I find answer: this code isn't testable.
Wherever you have new XXX(...) in a method under test, you are doomed.
More:
https://stackoverflow.com/a/7763207/2490611
I'm currently trying to build mocks for an interface (defined here as the Policy class) which only has one method, check; as seen below I'm basically just replacing it with a stub method which always returns a known value:
$mockBuilder = $this->getMockBuilder(Policy::class);
$allowMock = $mockBuilder->getMock();
$allowMock->method('check')->willReturn(Vote::ALLOW);
It registers as an object implementing Policy, as it should, but whenever the check method is called it only ever returns null. What am I doing wrong in my construction here?
Not sure if that solves your problem, but to mock interfaces, you should use getMockForAbstractClass():
$allowMock = $mockBuilder->getMockForAbstractClass();
Is there a way to create a mock class, as opposed to a mock object, with phpunit? I'm looking for a way to do dependency injection without having to explicitly pass every object a class might need to work with in the constructor (or wherever). Something that will return "true" for all of these cases:
public function testAAAA()
{
$foo = $this->getMock('foo', array('bar'));
var_dump(class_exists('foo', false));
var_dump(method_exists('foo', 'bar'));
var_dump(method_exists($foo, 'bar'));
}
This prints:
bool(true)
bool(false)
bool(true)
indicating that while it did successfully create a fake 'foo' class it did not bind a 'bar' method to it.
I am using phpunit 3.7.5.
I suspect that you don't actually want to do this (as you can disable constructors and so on with PHPUnit's mockbuilder, see the docs ), but assuming you do want or need to, this should do the trick:
$foo = $this->getMockBuilder('nonexistant')
->setMockClassName('foo')
->setMethods(array('bar'))
->getMock();
var_dump(class_exists('foo', false));
var_dump(method_exists('foo', 'bar'));
var_dump(method_exists($foo, 'bar'));
$cls = new ReflectionClass('foo');
var_dump($cls->hasMethod('bar'));
I'm honestly not sure about the specifics of why you need to specify different names (nonexistant and foo) above, but it seems to have to do with PHPUnit's behavior when the class being mocked doesn't exist yet, and having setMockClassName generate a class extending that class. Or Something. It's probably effectively working around a bug/edge-case -- this is odd usage of the library. You should be able to do the same thing through the getMock function alone, it's just uglier.
As an aside, it sounds like you should probably get familiar with php's reflection capabilities. It's not the most powerful reflection library out there, but it's pretty nice. I've used it to generate meta-information about required and optional fields for a class based on their constructor arguments and properties for a "model" library, where that meta-information is used to generate forms that accept the correct types of values. That is, generate typed forms without instances of the class the form is for, and without hand-writing a stupid amount of code -- it's about 100 lines in all for the entire feature. Obviously I don't know what you're trying to do, but from the small amount of info in your post, I'd guess it's closer to that type of meta-thing than not.
I am trying to test a class that manages data access in the database (you know, CRUD, essentially). The DB library we're using happens to have an API wherein you first get the table object by a static call:
function getFoo($id) {
$MyTableRepresentation = DB_DataObject::factory("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
...you get the idea.
We're trying to test this method, but mocking the DataObject stuff so that (a) we don't need an actual db connection for the test, and (b) we don't even need to include the DB_DataObject lib for the test.
However, in PHPUnit I can't seem to get $this->getMock() to appropriately set up a static call. I have...
$DB_DataObject = $this->getMock('DB_DataObject', array('factory'));
...but the test still says unknown method "factory". I know it's creating the object, because before it said it couldn't find DB_DataObject. Now it can. But, no method?
What I really want to do is to have two mock objects, one for the table object returned as well. So, not only do I need to specify that factory is a static call, but also that it returns some specified other mock object that I've already set up.
I should mention as a caveat that I did this in SimpleTest a while ago (can't find the code) and it worked fine.
What gives?
[UPDATE]
I am starting to grasp that it has something to do with expects()
I agree with both of you that it would be better not to use a static call. However, I guess I forgot to mention that DB_DataObject is a third party library, and the static call is their best practice for their code usage, not ours. There are other ways to use their objects that involve constructing the returned object directly. It just leaves those darned include/require statements in whatever class file is using that DB_DO class. That sucks because the tests will break (or just not be isolated) if you're meanwhile trying to mock a class of the same name in your test--at least I think.
When you cannot alter the library, alter your access of it. Refactor all calls to DB_DataObject::factory() to an instance method in your code:
function getFoo($id) {
$MyTableRepresentation = $this->getTable("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
function getTable($table) {
return DB_DataObject::factory($table);
}
Now you can use a partial mock of the class you're testing and have getTable() return a mock table object.
function testMyTable() {
$dao = $this->getMock('MyTableDao', array('getMock'));
$table = $this->getMock('DB_DataObject', ...);
$dao->expects($this->any())
->method('getTable')
->with('mytable')
->will($this->returnValue($table));
$table->expects...
...test...
}
This is a good example of a dependency in your code - the design has made it impossible to inject in a Mock rather than the real class.
My first suggestion would be to try and refactor the code to use an instance rather than a static call.
What's missing (or not?) from your DB_DataObject class is a setter to pass a prepared db object before calling the factory method. That way you can pass a mock or a custom db object (with the same interface) should the need arise.
In your test setup:
public function setUp() {
$mockDb = new MockDb();
DB_DataObject::setAdapter($mockDb);
}
The factory() method should return the mocked DB instance. If it's not already integrated into your class, you will probably have to refactor the factory() method as well to make it work.
Are you require/including the class file for DB_DataObject in your test case? If the class doesn't exist before PHPUnit tries to mock the object you can get errors like this.
With PHPUnit MockFunction extension plus runkit you can also mock static methods. Be careful, because it's monkey patching and therefore should only be used in extreme cases. Does not substitute good programming practices.
https://github.com/tcz/phpunit-mockfunction