How to provide data for Unit Tests in Delphi DUnit) ? For example, in PHP, you can do something like this:
public function URLProvider() {
return [
["https://helloacm.com"],
["https://codingforspeed.com"]
];
}
/**
* #dataProvider URLProvider
*/
public function test_url($url) {
$data = file_get_contents("$this->API?url=$url");
$result = json_decode($data, true);
$this->assertEquals(true, $result['result']);
$this->assertEquals(200, $result['code']);
}
With Spring4D DUnit extensions you can write something like that (look into the release/1.2 branch in the Tests\Source folder for the unit Spring.Testing).
program Tests;
uses
Spring.Testing,
TestInsight.DUnit;
type
TUrlTests = class(TTestCase)
public
class function UrlProvider: TArray<string>; static;
published
[TestCaseSource('UrlProvider')]
procedure TestUrl(const url: string);
end;
{ TUrlTests }
procedure TUrlTests.TestUrl(const url: string);
begin
// do whatever
end;
class function TUrlTests.UrlProvider: TArray<string>;
begin
Result := ['https://helloacm.com', 'https://codingforspeed.com'];
end;
begin
TUrlTests.Register;
RunRegisteredTests;
end.
You can either pass the name to the method within the same Testcase class or specify another class - the method itself must be a public static class function. The extension then will create different test cases for each parameter being passed. You can check the Unittests of Spring4D for other use cases.
In DUnit every single test case has to be a procedure without parameters. So there can't exist a mechanism for injecting arguments via custom attributes to test methods like you are doing it PHPUnit.
You should take a look at DUnitX instead where you can define tests like this:
[TestCase('https://helloacm.com')]
[TestCase('https://codingforspeed.com']
procedure TestUrl(const Url: String);
Related
Lets say there is such function:
function a()
{
$entity = $this->getEntity();
$entity->setSomePrivateVar();
$service = $this->getService();
$service->doSomething($entity);
}
I want to test that
$service->doSomething($entity);
is called with correct $entity.
The $entity calls setSomePrivateVar()
In real application code I have done something like this:
Get mock of entity and test that setSomePrivateVar is called.
Get mock of $service and test that doSomething() is called with parameter $entity.
Looks ok.
But the problem is - if I refactor code and first call doSomething() on service and then setSomePrivateVar() on $entity, test still passes.
But the function is now wrong, because doSomething is depending on $entity private field which is set by setSomePrivateVar().
For example I would refactor to this:
function a()
{
$entity = $this->getEntity();
$service = $this->getService();
$service->doSomething($entity);
// this line moved
$entity->setSomePrivateVar();
}
So it looks like PhpUnit is not checking $entity private fields. If it was for example array, then with() function would see that array passed is not same as expected.
So how do I test that doSomething() gets $entity in correct state (that setSomePrivateVar() was called on entity before passing it to doSomething() )?
Maybe it has something to do that $entity is mocked.
Update with real world example
public function setNotifyUsers(AnnualConsolidation $consolidation, $status)
{
$consolidation->setNotifyUsers($status); // if move this method after the flush(), tesst does not fail
$this->entityManager->persist($consolidation);
$this->entityManager->flush();
}
public function testNotifyUsers()
{
$consolidation = $this->getMockBuilder(AnnualConsolidation::class)
->setMethods(['setNotifyUsers'])
->getMock();
$consolidation
->expects($this->once())
->method('setNotifyUsers')
;
$this->entityManager
->expects($this->at(0))
->method('persist')
->with($consolidation)
;
$this->entityManager
->expects($this->at(1))
->method('flush')
;
/** #var AnnualConsolidation $consolidation */
$this->consolidationsService->setNotifyUsers($consolidation, true);
}
We were discussing if testing the setNotifyUsers method this way is even good. I was trying to test without hitting the database. One guy thinks that this might be needed to test with hitting database, because if refactoring method without changing logic, test might be needed to refactor. On the other hand - this method is not likely to be refactored that much.
But maybe also there is a way to just test that flush() is called after persist() without telling indexes, because in other examples, the indexes then might be needed to update after adding some call before persist and so might be too much work to keep tests working.
But for this topic - first I want to know how to make test fail - if I move setNotifyUsers after the flush(). Test is not failing. While if we would test with hitting database - we would see that $consolidation status is not updated.
One guy told to check, assert what is passed to the persist method. I did not try yet, but I am not sure if on mocked $consolidation this will be possible. Does mocked $consolidation has some state as real $consolidation would have?
As you say in your question
One guy told to check, assert what is passed to the persist method.
That would be the way to go but your code makes that rather hard and i think you should refactor a bit to make the code testable.
First your method is called "setNotifyUsers" but it does actually do 2 actions it calls the setNotifyUsers on the consolidation object and it saves/persists this data.
In my opinion those are 2 different actions that should belong in 2 different methods. It could help your test if you wrote it like :
public function setNotifyUsers(AnnualConsolidation $consolidation, $status) {
$consolidation->setNotifyUsers($status);
}
public function persistConsolidation(AnnualConsolidation $consolidation) {
$this->entityManager->persist($consolidation);
$this->entityManager->flush();
}
You could test the setNotifyUser and persistConsolidation sepperately and write a functional test for the part that calls these functions (the methods that use the consolidationsService)
then you can use the at() functionality to see if those functions are called in the correct order.
But:
Second you give both the state as the consolidation to this function with as only reason to add those together. i don't think something like that belongs in the service but rather in the method
calling that service.
Moving that functionality will again give you trouble as you cannot test the order in which they are called.
But you don't need to use the mockBuilder to make a test double.
Instead of using $this->getMockBuilder you can also create a FakeConsolidation that will actually hold the data for you
Then you will also need a mock for the AnnualConsolidation because you want to be able to check if the value was correctly set.
class FakeConsolidation extends AnnualConsolidation {
protected $id;
proteced $status;
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function setNotifyUsers($status) {
$this->status = $status;
}
public function shouldNotifyUsers() {
$this->status
}
}
Now because you will give an object to the persist that has a state we can check that state in the "with" part.
Of course, I do not know exactly how your code is structured so I made some assumptions just adapt where needed and use the interfaces that you have.
Like this you could even test the code as you presented it in this question:
class SomethingTest extends PHPUnit_Framework_TestCase {
private $consolidationsService;
private $entityManager;
/**
* {#inheritdoc}
*/
public function setUp() {
$this->entityManager = $this->getMockBuilder(EntityManager::class)->getMock();
$this->consolidationsService = new ConsolidationsService($this->entityManager);
}
public function testNotifyUsers() {
$consolidation = new FakeConsolidation();
$consolidation->setId(1);
$this->entityManager
->expects($this->at(0))
->method('persist')
->with($this->callback(
function($savedConsolidation) {
return $savedConsolidation->shouldNotifyUsers() === true;
}
));
$this->entityManager
->expects($this->at(1))
->method('flush');
/** #var AnnualConsolidation $consolidation */
$this->consolidationsService->setNotifyUsers($consolidation, TRUE);
}
}
Now when you move the setNotifyUsers below the persist
with($this->callback(
function($savedConsolidation) {
return $savedConsolidation->shouldNotifyUsers() === true;
}
));
Your test will fail because the status is not set yet.
Imagine I have the following class.
class SomeClass {
public function shortcutMethod($arg1) {
return $this->method($arg1, 'something');
}
public function method($arg1, $arg2) {
// some stuff
}
}
So the shortcutMethod is a shortcut to the other method. Let us say I want to write a test that given and $arg1 the shortcutMethod will correctly call method with the correct arguments.
So far I think I figured I need to mock the class to expect a call to method with some arguments and then call shortcutMethod on the mock object like so (note I am using Mockery).
$mock = m::mock("SomeClass");
$mock = $mock->shouldReceive('method')->times(1)->withArgs([
'foo',
'something'
]);
$mock->shortcutMethod('foo');
This results in an exception like so shortcutMethod() does not exist on this mock object.
Did I misunderstand the usage for mocking? I understand it makes more sense for objects that are dependency injected into the class, but what in this scenario? How would you go about it? And perhabs more importantly, is this sort of testing useless, and if so, why?
You should use mocking to mock out the dependencies of the class under test, not the class under test itself. After all, you are trying to test the real behavior of your class.
Your example is a little basic. How you would test such a class would depend on what your method function does. If it returns a value that is in turn returned by shortCutMethod then I would say that your should just be asserting the output of shortCutMethod. Any dependencies within the method function should be mocked (methods belonging to other classes). I'm not that familiar with mockery, but I've given a tweaked version of your example a go.
class SomeClass {
private $dependency;
public function __construct($mockedObject) {
$this->dependency = $mockedObject;
}
public function shortcutMethod($arg1) {
return $this->method($arg1, 'something');
}
public function method($arg1, $arg2) {
return $this->dependency->mockedMethod($arg1, $arg2);
}
}
$mock = m::mock("mockedClass");
$mock->shouldReceive('mockedMethod')->times(1)->withArgs([
'foo',
'something'
])->andReturn('returnedValue');
$testCase = new SomeClass($mock);
$this->assertEquals(
'returnedValue',
$testCase->shortcutMethod('foo')
);
Having said that, it is possible to partially mock your class under test so that you can test the real behavior of the shortCutMethod function but mock out the method function to assert that it is called with the expected arguments. Have a look at partial mocks.
http://docs.mockery.io/en/latest/reference/partial_mocks.html
PHPUnit has setup and tearDown events that run, respectively, before and after each test within a test case. In my specific scenario, I also want to run something like a testCaseSetup and testCaseTearDown. Is that possible?
Current solution looks like this:
<?php
class MyTestCase extends \PHPUnit_Framework_TestCase
{
public function __construct($name = NULL, array $data = array(), $dataName = '')
{
// My test case setup logic
parent::__construct($name, $data, $dataName);
}
public function __destruct()
{
// My test case tear down logic
}
}
But it seems far from optimal for the following reasons:
I have to redeclare PHPUnit_Framework_TestCase construct and redirect any arguments. IF PHPUnit constructor is changed on a version update, my test case will stop.
Probably PHPUnit_Framework_TestCase was not declared to be used like this.
I would like to know if there are better solutions. Any ideas?
Yes, there are special methods for that purpose: setUpBeforeClass and tearDownAfterClass.
class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
public static function setUpBeforeClass()
{
// do sth before the first test
}
public static function tearDownAfterClass()
{
// do sth after the last test
}
I am unit testing my Laravel 4 Controller by mocking my repository that the controller expects. The problem is with the "store" function. This is the function that is called by Laravel when I do a POST to the given controller. The function gets called, but it is expected itemData as an input but I don't know how to provide that. Here is what I've tried:
ItemEntryController
class ItemEntryController extends BaseController
{
protected $itemRepo;
public function __construct(ItemEntryRepositoryInterface $itemRepo)
{
$this->itemRepo = $itemRepo;
}
public function store()
{
if(Input::has('itemData'))
{
$data = Input::get('itemData');
return $this->itemRepo->createAndSave($data);
}
}
}
Test class
<?php
use \Mockery as m;
class ItemEntryRouteAndControllerTest extends TestCase {
protected $testItemToStore = '{"test":12345}';
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('Storage\ItemEntry\ItemEntryRepositoryInterface');
}
public function mock($class)
{
$mock = m::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testItemStore()
{
Input::replace($input = ['itemData' => $this->testItemToStore]);
$this->mock
->shouldReceive('createAndSave')
->once()
->with($input);
$this->call('POST', 'api/v1/tools/itementry/items');
}
Well, you got a few options.
Integration testing
You may want to follow the unit testing docs, which actually has a call() method which allows you set all of this. This bootstraps the app and will use your databases, etc.
This is more of an integration test than unit test, as it uses your actual class implementations.
This may actually be preferable, as Unit testing controllers may not actually make much sense (it doesn't do much, in theory, but call other already-unit-tested classes). But this gets into unit testing vs integration testing vs acceptance testing and all the nuances that apply therein. (Read up!)
Unit Testing
If you're actually looking to unit test, then you need to make your controller unit-testable (ha!). This (likely) means injecting all dependencies:
class ItemEntryController extends BaseController
{
protected $itemRepo;
// Not pictured here is actually making sure an instance of
// Request is passed to this controller (via Service Provider or
// IoC binding)
public function __construct(ItemEntryRepositoryInterface $itemRepo, Request $input)
{
$this->itemRepo = $itemRepo;
$this->request = $input;
}
public function store()
{
if($this->input->has('itemData'))
{
// Get() is actually a static method so we use
// the Request's way of getting the $_GET/$_POST variables
// see note below!
$data = $this->input->input('itemData');
return $this->itemRepo->createAndSave($data);
}
}
}
Sidenote: The Input facade is actually an instance of Request objet with an extra static method get()!
So now that we aren't using Input any longer, and are injecting the Request object, we can unit test this class by mocking the Request object.
Hope that helps!
Im testing with PHPUnit and my test fails on a function. But i don't know why.
The function i want to mock:
public function subscribe($email)
{
$message = new SubscribeMessage();
$message->setEmailaddress($email);
$message->setLocale(Locale::getDefault());
$this->getAmqpProducer()->publish($message, 'newsletter-subscribe');
return true;
}
and my Unit test:
public function testSubscribeSendsAmqpMessage()
{
$email = 'email#email.nl';
$locale = 'nl';
$this->amqpProducerMock
->shouldReceive('publish')
->once()
->with(
\Mockery::on(
function ($message, $routingkey) use (&$publishedMessage) {
$publishedMessage = $message;
return $routingkey == 'newsletter-subscribe';
}
)
);
$this->service->subscribe($email, $locale);
}
but the test says:
Mockery\Exception\NoMatchingExpectationException : No matching handler found for AcsiRabbitMq\Producer\Producer::publish(AcsiNewsletter\RabbitMq\Message\SubscribeMessage, "newsletter-subscribe"). Either the method was unexpected or its arguments matched no expected argument list for this method
How can i fix my Unit test? Or how can i refactor my test?
You Mock the subscribe, not the internal publish. When you run the test and call ->subscribe, it will attempt to execute the code in the class. Therefore, it will try to run the subscribe() method, which you appear to have a strange reference to your Mock.
Normally, your test will mock the subscribe, so you can return a value for the assert test, which is hard coded.
You appear to have tried to mock the GetAmqpProducer() object that is in your regular code. You need to either be able to pass the mock object to be used into your class, or to be able to assign it.
Simplified Example:
class Email
{
private $MsgObject;
// Constructor Injection
public __construct(SubscribeMessage $MessageObject)
{
$this->MsgObject = $MessageObject;
...
}
// Setter Injection
public function SetSubscribeMessage(Subscribe $MessageObject)
{
$this->MsgObject = $MessageObject;
}
public function setEmailaddress($email)
{
$this->MsgObject->emailAddress = $email;
...
}
public function setLocale($Locale)
{
$this->MsgObject->Locale = $Locale;
...
}
...
}
Your class sample above has too many internal objects and dependencies to be tested as such, since the test will actually call these. You would use Dependency Injection to pass the objects with known state, and have them return properly.
Please note, I am not showing how to do this in Mockery, as I do not use it, but this simple example should help you understand what I am trying to express.
So a simple test might look like:
public function testSubscribeMessage()
{
$email = 'email#email.nl';
$this->Mock(
->shouldReceive('setEmailAddress')
->once()
->will_return($email)
);
$SubscribeMessage = new SubscribeMessage($this->Mock);
$SetEmail = $SubscribeMessage->setEmailAddress($email);
$this->assertEquals($email, $SetEmail);
}