Test factory method - php

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

Related

Adding interface to mock with Mockery (hard dependency)

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.

mockery overload class missing methods

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.

Mocking a method on dynamically allocated instance?

Background: I'm working on an MVC framework for some practice, and want to make sure everything is 100% unit tested.
The setup currently is to have an instance of the application class (Ex_App). The main script asks a Dispatcher/Router for a controller name. This controller name is the name of a class implementing Ex_Controller. The result is returned as an instance of Ex_Dispatch_Result. This result is passed to the Ex_App instance using an invokeController($dispatchResult) function.
And this is where magic happens. The listing below is an excerpt:
$controllerName = $dispatchResult->getControllerName();
... checks for validaty of class name ...
$controller = new $controllerName();
$controller->prepare($this);
I'm using PHPUnit to do my unit testing, and am able to mock the dispatch result, correctly check that validating the class name of the controller works. The problem is how to check if prepare is called.
I'd like to do something similar to:
$mockController = $this->getMockBuilder('Ex_Controller')
->setMockClassName('Invoke_Correct_Controller')
->getMock();
$mockController->expects($this->once())->method('prepare');
However since a new instance of Invoke_Correct_Controller is created upon calling invokeController, it will not be this mock and thus the expects() call is completely irrelevant.
I could make the Ex_Dispatch_Result class responsible for returning a controller and testing that, but before returning an instance I will need to verify the correctness of the class name and in my opinion that responsibility should be with the Ex_App class and not the "dumb shell" Ex_Dispatch_Result class.
Is there something I am missing in the PHPUnit framework that I could use to test the code here, or some useful pattern that could work in my instance? I feel passing around controller names scales way better than passing around instances of controllers from the start, requiring the initialization of every possible controller. So, I kinda want to stick to passing around names and using the Ex_App as a factory for the controller instance.
Maybe I'm just over-thinking part of this problem, but that happens sometimes. It's why a fresh look by a third party often works :-)
There are couple of things you could do:
Extract controller creation logic to separate class e.g. ControllerFactory, and then mock controller factory instance, so that it returns your $mockController.
Extract controller creation logic to separate method and use partial mocking.
Return $mockController from $dispatchResult->getControllerName(), which probably requires mocking of $dispatchResult or even something else.
If you want more detailed answer, please provide more code samples of your classes and methods.

unit testing and Static methods

Reading up and picking up on unit testing, trying to make sense of the following post on that explains the hardships of static function calls.
I don't clearly understand this issue. I have always assumed static functions were a nice way of rounding up utility functions in a class. For example, I often use static functions calls to initialise, ie:
Init::loadConfig('settings.php');
Init::setErrorHandler(APP_MODE);
Init::loggingMode(APP_MODE);
// start loading app related objects ..
$app = new App();
// After reading the post, I now aim for this instead ...
$init = new Init();
$init->loadConfig('settings.php');
$init->loggingMode(APP_MODE);
// etc ...
But, the few dozen tests I had written for this class are the same. I changed nothing and they still all pass. Am I doing something wrong?
The author of the post states the following:
The basic issue with static methods is they are procedural code. I have no idea how to unit-test procedural code. Unit-testing assumes that I can instantiate a piece of my application in isolation. During the instantiation I wire the dependencies with mocks/friendlies which replace the real dependencies. With procedural programing there is nothing to “wire” since there are no objects, the code and data are separate.
Now, I understand from the post that static methods create dependencies, but don't grasp intuitively why one cannot test the return value of a static method just as easily as a regular method?
I will be avoiding static methods, but I would of liked having an idea of WHEN static methods are useful, if at all. It seems from this post static methods are just about as evil as global variables and should be avoided as much as possible.
Any additional information or links on the subject would be greatly appreciated.
Static methods themselves aren't harder to test than instance methods. The trouble arises when a method--static or otherwise--calls other static methods because you cannot isolate the method being tested. Here is a typical example method that can be difficult to test:
public function findUser($id) {
Assert::validIdentifier($id);
Log::debug("Looking for user $id"); // writes to a file
Database::connect(); // needs user, password, database info and a database
return Database::query(...); // needs a user table with data
}
What might you want to test with this method?
Passing anything other than a positive integer throws InvalidIdentifierException.
Database::query() receives the correct identifier.
A matching User is returned when found, null when not.
These requirements are simple, but you must also setup logging, connect to a database, load it with data, etc. The Database class should be solely responsible for testing that it can connect and query. The Log class should do the same for logging. findUser() should not have to deal with any of this, but it must because it depends on them.
If instead the method above made calls to instance methods on Database and Log instances, the test could pass in mock objects with scripted return values specific to the test at hand.
function testFindUserReturnsNullWhenNotFound() {
$log = $this->getMock('Log'); // ignore all logging calls
$database = $this->getMock('Database', array('connect', 'query');
$database->expects($this->once())->method('connect');
$database->expects($this->once())->method('query')
->with('<query string>', 5)
->will($this->returnValue(null));
$dao = new UserDao($log, $database);
self::assertNull($dao->findUser(5));
}
The above test will fail if findUser() neglects to call connect(), passes the wrong value for $id (5 above), or returns anything other than null. The beauty is that no database is involved, making the test quick and robust, meaning it won't fail for reasons unrelated to the test like network failure or bad sample data. It allows you to focus on what really matters: the functionality contained within findUser().
Sebastian Bergmann agrees with Misko Hevery and quotes him frequently:
Stubbing and Mocking Static Methods
Unit-Testing needs seams, seams is where we prevent the execution of normal code path and is how we achieve isolation of the class under test. Seams work through polymorphism, we override/implement class/interface and then wire the class under test differently in order to take control of the execution flow. With static methods there is nothing to override. Yes, static methods are easy to call, but if the static method calls another static method there is no way to override the called method dependency.
The main issue with static methods is that they introduce coupling, usually by hardcoding the dependency into your consuming code, making it difficult to replace them with stubs or mocks in your Unit-Tests. This violates the Open/Closed Principle and the Dependency Inversion Principle, two of the SOLID principles.
You are absolutely right that statics are considered harmful. Avoid them.
Check the links for additional information please.
Update: note that while statics are still considered harmful, the capability to stub and mock static methods has been removed as of PHPUnit 4.0
I do not see any problem when testing static methods (at least none that doesn't exists in non-static methods).
Mock objects are passed to classes under test using dependency injection.
Mock static methods can be passed to classes under test using a suitable autoloader or manipulating the include_path.
Late static binding deals with methods calling static methods in the same class.

Mock Objects in PHPUnit to emulate Static Method Calls?

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

Categories