Unit test custom doctrine repository - unit-testing

I have a custom entity repository. For example, it looks like this:
namespace Foo\Repository;
use Doctrine\ORM\EntityRepository;
class Article extends EntityRepository
{
public function findRecent($limit)
{
$qb = $this->createQueryBuilder('a');
$qb->andWhere('a.publishDate IS NOT NULL')
->orderBy('a.publishDate', 'DESC')
->setMaxResults($limit);
return $qb->getQuery()->getResult();
}
}
I want to test in this case:
There is an ORDER BY in "recent"
There is a limit
The entity must have a publish date
I do not want to validate the SQL output of the query builder, since Doctrine can change the SQL between different versions. That will break my unit test. Therefore, my idea was this:
Create a mock of my repository
Create a mock of the query builder
Make sure $this->createQueryBuilder('a') returns the mocked query builder
Test for method calls on the query builder
In code:
namespace FooTest\Repository;
use PHPUnit_Framework_TestCase as TestCase;
class ArticleRepositoryTest extends TestCase
{
protected $qb;
protected $repository;
public function setUp()
{
$this->qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder')
->disableOriginalConstructor()
->getMock();
$this->repository = $this->getMockBuilder('Foo\Repository\Article')
->disableOriginalConstructor()
->getMock();
$this->repository->expects($this->once())
->method('createQueryBuilder')
->with($this->equalTo('a'))
->will($this->returnValue($this->qb));
}
public function testFindRecentLimitsToGivenLimit()
{
$limit = '1';
$this->qb->expects($this->any())
->method('setMaxResults')
->with($this->equalTo($limit));
$this->repository->findRecent($limit);
}
public function testFindRecentOrdersByPublishDate()
{
$this->qb->expects($this->any())
->method('andWhere')
->with($this->equalTo('a.publishDate'), $this->equalTo('DESC'));
$this->repository->findRecent(1);
}
}
This findRecent() call however never calls createQueryBuilder internally. PhpUnit points out:
FooTest\Repository\ArticleRepositoryTest::testFindRecentLimitsToGivenLimit
Expectation failed for method name is equal to when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
I think I did something wrong in creating the repository mock. How can I make sure this approach works? Or if there is a better alternative, what is that?

It looks to me like you are mocking the Repository you are trying to test, so findRecent() is indeed mocked and will return null.
You should be able to use the real repository instance.

The solution I found to testing subclassed repositories is to add a call to setMethodsExcept() when building the mock.
So you would modify your code within setUp() above like so:
$this->repository = $this->getMockBuilder('Foo\Repository\Article')
->disableOriginalConstructor()
->setMethodsExcept([
// Insert any overridden/implemented functions here, in your case:
'findRecent',
])
->getMock();

Related

cannot stub ReactiveMongoRepository using Mockito.when

I'm new to reactive programming. I want to write some test cases for a reactive mongo repository. I tried to stub some query methods and use step-verifier to check the response, but my test gets fail .
ItemReactiveRepository.java
public interface ItemReactiveRepository extends ReactiveMongoRepository<Item, String> {
Mono<Item> findByDescription(String description);
}
Item.java
#Document
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Item {
#Id
private String id;
private String description;
private Double price;
}
ItemReactiveRepositoryTest.java
#DataMongoTest
#RunWith(SpringRunner.class)
public class ItemReactiveRepositoryTest {
#Autowired
private ItemReactiveRepository itemReactiveRepository;
#Test
public void findById() {
Item itemForTest = new Item("ABC", "Samsung TV", 405.0);
Mockito.when(itemReactiveRepository.findById("ABC")).thenReturn(Mono.just(itemForTest));
StepVerifier.create(itemReactiveRepository.findById("ABC"))
.expectSubscription()
.expectNextMatches(item -> item.getPrice() == 405.0)
.verifyComplete();
}
}
Error I receive when running test
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
inside when() you don't call method on mock but on some other object.
Are there any limitations to use stubbing when test reactive streams? Or any other standard mechanism to test above scenarios?
Instead of using #Autowired you have to prepare mock for Repository from import org.mockito.Mock;
#Mock
ItemReactiveRepository itemReactiveRepository;
As #Thomas has mentioned, you are not mocking instead using the actual MongoDB using DataMongoTest instead you have to get rid of this and just mock the methods and test your service layer. Autowired is expecting all default configuration and a bean which is prepared by the container for you to use which is not the case, you have to mock and use.
ItemReactiveRepository itemReactiveRepository=org.mockito.Mockito.mock(ItemReactiveRepository.class);
This worked for me.

Mock view helper in Zend Framework 3 in PHPUnit test

I want to test a specific controller action in Zend Framework 3. Because I use ZfcUser (https://github.com/ZF-Commons/ZfcUser) and Bjyauthorize (https://github.com/bjyoungblood/BjyAuthorize) I need to mock some view helpers. For example I need to mock isAllowed view helper and let it return true always:
class MyTest extends AbstractControllerTestCase
{
public function setUp()
{
$this->setApplicationConfig(include 'config/application.config.php');
$bootstrap = \Zend\Mvc\Application::init(include 'config/application.config.php');
$serviceManager = $bootstrap->getServiceManager();
$viewHelperManager = $serviceManager->get('ViewHelperManager');
$mock = $this->getMockBuilder(IsAllowed::class)->disableOriginalConstructor()->getMock();
$mock->expects($this->any())->method('__invoke')->willReturn(true);
$viewHelperManager->setService('isAllowed', $mock);
$this->getApplication()->getServiceManager()->setAllowOverride(true);
$this->getApplication()->getServiceManager()->setService('ViewHelperManager', $viewHelperManager);
}
public function testViewAction()
{
$this->dispatch('/myuri');
$resp = $this->getResponse();
$this->assertResponseStatusCode(200);
#$this->assertModuleName('MyModule');
#$this->assertMatchedRouteName('mymodule/view');
}
}
In my view.phtml (which will be rendered by opening/dispatching /myuri uri) I call the view helper $this->isAllowed('my-resource').
But I got response code 500 with failing exception when executing testViewAction():
Exceptions raised:
Exception 'Zend\ServiceManager\Exception\ServiceNotFoundException' with message 'A plugin by the name "isAllowed" was not found in the plugin manager Zend\View\HelperPluginManager' in ../vendor/zendframework/zend-servicemanager/src/AbstractPluginManager.php:131
How can I inject my isAllowed mock to the view helper manager in a way, that let the test case (testViewAction / $this->dispatch()) pass.
As stated in the previous answer already, we need to override the ViewHelper within the ViewHelperManager in the application object. The following code shows how this could be achieved:
public function setUp()
{
$this->setApplicationConfig(include 'config/application.config.php');
$bootstrap = \Zend\Mvc\Application::init(include 'config/application.config.php');
$serviceManager = $bootstrap->getServiceManager();
// mock isAllowed View Helper of Bjyauthorize
$mock = $this->getMockBuilder(IsAllowed::class)->disableOriginalConstructor()->getMock();
$mock->expects($this->any())->method('__invoke')->willReturn(true);
// inject the mock into the ViewHelperManager of the application
$this->getApplication()->getServiceManager()->get('ViewHelperManager')->setAllowOverride(true);
$this->getApplication()->getServiceManager()->get('ViewHelperManager')->setService('isAllowed', $mock);
}
ViewHelperManager is an other instance of service manager. and it's not allowed to override source code. Can you try "setAllowOverride" before "setService" method?

Mocking a REST Datasource in a Model TestCase for CakePHP

I am using the CakePHP-ReST-DataSource-Plugin Datasource for hitting a RESTful service in my model. This implies that the models will not have a database connection.
I have successfully accessed the services and would now like to write unit tests for the models. This is proving to be a daunting task since I cannot succeed to mock the datasource so that I do not hit the actual remote Service but rather return expected results for the tests.
<?php
App::uses('KnowledgePoint', 'Model');
class KnowledgePointTest extends CakeTestCase{
public $fixtures = array('app.knowledgepoint');
public $useDbConfig = 'RestTest';
private $KnowledgePoint;
public function setUp() {
parent::setUp();
$this->KnowledgePoint = ClassRegistry::init('KnowledgePoint');
/**
* This is the confusing part. How would I mock the datasource
so that I can mock the request method which returns the data
from the api?
*/
$this->KnowledgePoint->DataSource = $this->getMockForModel(
'RestSource',array('request'));
}
public function tearDown() {
parent::tearDown();
}
}
I would like to be able to mock the datasource and stub the request method to return data that would normally be returned from the remote service.
Kind regards,
Roland
Mocking the model and its getDataSource() method so that it returns your mocked datasource should theoretically work. Here's an example
App::uses('RestSource', 'Rest.Model/Datasource');
$DataSource = $this->getMock('RestSource', array('request'), array(array()));
$DataSource
->expects($this->any())
->method('request')
->will($this->returnValue('some custom return value'));
$Model = $this->getMockForModel('KnowledgePoint', array('getDataSource'));
$Model
->expects($this->any())
->method('getDataSource')
->will($this->returnValue($DataSource));
$Model->save(/* ... */);
In case you are wondering about the array(array()) for the datasource mock, this is required as the RestSource constructor doesn't supply a default value for the first argument (unlike the parent constructor).

Issue testing Laravel Controller with Mockery | trying to get property of non-object

I'm very new to testing controllers and I'm running into a problem with a method(). I believe I'm either missing something in my test or my Controller / Repository is designed incorrectly.
The application I'm writing is basically one of those secure "one time" tools. Where you create a note, the system provides you with a URL, once that url is retrieved the note is deleted. I actually have the application written but I am going back to write tests for practice (I know that's backwards).
My Controller:
use OneTimeNote\Repositories\NoteRepositoryInterface as Note;
class NoteController extends \Controller {
protected $note;
public function __construct(Note $note)
{
$this->note = $note;
}
public function getNote($url_id, $key)
{
$note = $this->note->find($url_id, $key);
if (!$note) {
return \Response::json(array('message' => 'Note not found'), 404);
}
$this->note->delete($note->id);
return \Response::json($note);
}
...
I've injected my Note interface in to my controller and all is well.
My Test
use \Mockery as M;
class OneTimeNoteTest extends TestCase {
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('OneTimeNote\Repositories\EloquentNoteRepository');
}
public function mock($class)
{
$mock = M::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testShouldReturnNoteObj()
{
// Should Return Note
$this->mock->shouldReceive('find')->once()->andReturn('test');
$note = $this->call('GET', '/note/1234567890abcdefg/1234567890abcdefg');
$this->assertEquals('test', $note->getContent());
}
}
...
The error I'm getting
1) OneTimeNoteTest::testShouldReturnNoteObj
ErrorException: Trying to get property of non-object
/Users/andrew/laravel/app/OneTimeNote/Controllers/NoteController.php:24
Line 24 is in reference to this line found in my controller:
$this->note->delete($note->id);
Basically my abstracted repository method delete() obviously can't find $note->id because it really doesn't exist in the testing environment. Should I create a Note within the test and try to actually deleting it? Or would that be something that should be a model test? As you can see I need help, thanks!
----- Update -----
I tried to stub the repository to return a Note object as Dave Marshall mentioned in his answer, however I'm now receiving another error.
1) OneTimeNoteTest::testShouldReturnNoteObj
BadMethodCallException: Method Mockery_0_OneTimeNote_Repositories_EloquentNoteRepository::delete() does not exist on this mock object
I do have a delete() method in my repository and I know it's working when I test my route in the browser.
public function delete($id)
{
Note::find($id)->delete();
}
You are stubbing the note repository to return a string, PHP is then trying to retrieve the id attribute of a string, hence the error.
You should stub the repository to return a Note object, something like:
$this->mock->shouldReceive('find')->once()->andReturn(new Note());
Building upon Dave's answer, I was able to figure out what my problem is. I wasn't mocking the delete() method. I didn't understand the need to mock each individual method in my controller that would be called.
I just added this line:
$mock->shouldReceive('delete')->once()->andReturnNull();
Since my delete method is just deleting the note after it is found, I went ahead and mocked it but set it to return null.

PHPunit mock - call a function in a returned mock

I'm pretty new to phpunit and mocking, and I want to test a Listener in my symfony2 project, what is a kernel exception listener.
This is the class I want to test:
public function onKernelException(GetResponseForExceptionEvent $event)
{
$code = $event->getException()->getCode();
if($code == 403)
{
$request = $event->getRequest();
$session = $request->getSession();
$session->getFlashBag()->add('notice', 'message');
$session->set('hardRedirect', $request->getUri());
}
}
And first I just wanted to test, so nothing happens if the code is 404, this is the test I wrote:
public function testWrongStatusCode()
{
$exceptionMock = $this->getMock('Exception')
->expects($this->once())
->method('getCode')
->will($this->returnValue('404'));
$eventMock = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent')
->disableOriginalConstructor()
->getMock();
$eventMock->expects($this->once())
->method('getException')
->will($this->returnValue($exceptionMock));
//here call the listener
}
but PHPunit say, getCode function was never called.
You can't use "chaining" as you've tried. The reason is that methods getMock and will return different objects. That's why you lose your real mock object. Try this instead:
$exceptionMock = $this->getMock('\Exception');
$exceptionMock->expects($this->once())
->method('getCode')
->will($this->returnValue('404'));
Edit
Ok. The problem is you cannot mock getCode method because it's final and it's impossible to mock final and private methods with PHPUnit.
My suggestion is: just prepare an exception object you want, and pass it as returned value to event mock:
$exception = new \Exception("", 404);
(...)
$eventMock->expects($this->once())
->method('getException')
->will($this->returnValue($exception));
This is how I mock the getCode() function. It actually gets called from the ResponseInterface::getStatusCode() function, so that is what you need to mock:
$guzzle->shouldReceive('get')
->once()
->with(
$url
)
->andThrows(new ClientException(
"",
Mockery::mock(RequestInterface::class),
Mockery::mock(ResponseInterface::class, [
'getStatusCode' => 404,
]),
));
You can use mockery library with PHPUnit, which is great tool and makes life easier.
$exceptionMock = \Mockery::mock('GetResponseForExceptionEvent');
$exceptionMock->shouldReceive('getException->getCode')->andReturn('404');
Check out documentation for more... and I hope you will love it.