PHPUnit is reporting errors instead of recognizing assertFalse() - unit-testing

I am attempting to test a simple Laravel model which has required "password" and "email" properties. My test reads as follows…
public function testEmailIsRequired() {
$user = new User;
$user->password = 'derp';
// should not save
$this->assertFalse($user->save());
}
Rather than correctly agree that "this doesn't work" and a successful test, I’m getting…
1) UserTest::testEmailIsRequired
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation:
19 NOT NULL constraint failed: users.email
(SQL: insert into "users" ("password", "updated_at", "created_at")
values (derp, 2014-09-26 15:27:07, 2014-09-26 15:27:07))
[...]
FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
I’m afraid that I’m a total novice here. It seems to me that PHPUnit ought to be suppressing the database driver’s own error reporting and just accepting "false"… help?

Your test is looking for the function to return false. Any other errors generated in your function will still be returned, since you are calling the function.
public function testEmailIsRequired()
{
$user = new User;
$user->password = 'derp';
// should not save
$this->assertFalse($user->save());
}
public function testMockSaveWorks()
{
$Mock = $this->getMock('User', array('save'));
// Set up the expectation for the getResult() method
$Mock->expects($this->any())
->method('save')
->will($this->returnValue(true));
$this->assertTrue($Mock->save());
}
class User()
{
...
public function save()
{
if( is_null($this->email) )
return false;
...
return true;
}
}
You should then use Mocks to remove the actual save() function and its tie in to your database, and have the mock return true to pretend to save the record to show that you have tested the module.

Related

Spock interaction verification ignoring call to Mock method

The following Spock test is failing to not counting the call to the Mock method:
def setup() {
mojo = new PactCreateVersionTagMojo()
mojo.pactBrokerUrl = 'http://broker:1234'
mojo.pacticipant = 'test'
mojo.pacticipantVersion = '1234'
mojo.tag = 'testTag'
}
def 'calls pact broker client with mandatory arguments'() {
given:
mojo.brokerClient = Mock(PactBrokerClient)
when:
mojo.execute()
then:
notThrown(MojoExecutionException)
1 * mojo.brokerClient.createVersionTag(
'test', '1234', 'testTag')
}
You can find it here.
The SUT code, removing the argument validation code, is:
class PactCreateVersionTagMojo : PactBaseMojo() {
override fun execute() {
...
createVersionTag()
}
private fun createVersionTag() =
brokerClient!!.createVersionTag(pacticipant!!, pacticipantVersion.orEmpty(), tag.orEmpty())
You can find it here.
The error is as follows:
I have a very similar example on the same project that passes just fine:
def 'passes optional parameters to the pact broker client'() {
given:
mojo.latest = 'true'
mojo.to = 'prod'
mojo.brokerClient = Mock(PactBrokerClient)
when:
mojo.execute()
then:
notThrown(MojoExecutionException)
1 * mojo.brokerClient.canIDeploy('test', '1234',
new Latest.UseLatest(true), 'prod') >> new CanIDeployResult(true, '', '')
}
override fun execute() {
...
val result = brokerClient!!.canIDeploy(pacticipant!!, pacticipantVersion.orEmpty(), latest, to)
}
You can find the test above here and the SUT here.
I have investigated the call that happens during the test, and it seems as expected.
Additionally, I try to create the verification with wildcard argument constraints, but it still didn't work.
It seems to me that I have misconfigured my test, but I can't spot the difference between the test that passes and my failing test.
Your fun createVersionTag(..) looks like this:
fun createVersionTag(
pacticipant: String,
pacticipantVersion: String,
tag: String) {
}
I do not speak Kotlin, but I think you ought to open the method because otherwise it is final, which means it cannot be overridden by a subclass and thus not be mocked or stubbed by conventional means. This is also the difference to open fun canIDeploy(..).

phpunit testing methods with $this values

OK, probably a bad example. Here is an actual example. I would be wanting to unit test the total() method from a shopping cart class to see if the value is what I think it should be, which uses the getAll() (cart items), subTotal() and shipping() methods. Is total() something you could test using PHPUnit ?
class Cart extends ObjectModel
{
protected $uniqueID = 10;
public function getAll()
{
return $this->execute("SELECT *, cart.id AS cart_id FROM cart LEFT JOIN products ON products.id = cart.product_id WHERE cart.unique_id = ? AND cart.quantity > '0' AND cart.deleted_at IS NULL ", [$this->uniqueID] );
}
public function subTotal()
{
$subTotal = 0;
foreach($this->getAll() as $row){
$subTotal += ( $row->quantity * $row->cart_price );
}
return $subTotal;
}
public function total()
{
return $this->subTotal() + $this->shipping();
}
public function shipping()
{
if(isset($_SESSION[SALT.'shipping']) && $_SESSION[SALT.'shipping'] != ''){
return $_SESSION[SALT.'shipping'];
}
/* OR RETURN DEFAULT IF SHIPPING IS NOT SET */
return 0;
}
}
The execute method is as follows, there is no constructor method for the ObjectModel class
public function execute($query, array $array) {
$query = Db::conn()->prepare($query);
$query->execute($array);
return $query->fetchAll(PDO::FETCH_OBJ);
}
Db::conn() is just the PDO database handle.
The class is not too well designed for unit-testing because it is tightly coupled to the database. In a unit-test, you want to test the subject-under-test in isolation of external components (here: the database). Usually, you mock or stub the external components with a test double.
Because the DB instance is hardcoded in the execute method of the ObjectModel, you cannot easily replace it with a test double. You could turn the test into an integration test and change the DB connection to a test database which you then populate with expected values in the tests setup method.
If you don't want to use the database at all, you can self-shunt the subject-under-test. This means, you use a test double of the subject-under-test itself and make execute return the expected values for your test case.

Doctrine ORM produce different output when using manual 'select'

I'm a beginner with Doctrine ORM (v2.5.5) and Silex (v2.0.4)/Symfony (v3.1.6). I need to output my Date field to the YYYY-MM-DD format. Let's say I have this annotation and getter method on my Entity:
// src/App/Entity/Tnkb.php (simplified)
// 'expire' field
/**
* #ORM\Column(type="date")
*/
protected $expire;
// getter
public function getExpire()
{
return !is_object($this->expire) ? new \DateTime() : $this->expire->format('Y-m-d');
}
Here's my simplified controller for debugging purpose:
$app->get('/debug', function() use ($app) {
$tnkbRepo = $app['orm.em']->getRepository('\App\Entity\Tnkb');
$normalizer = new \Symfony\Component\Serializer\Normalizer\ObjectNormalizer();
$encoder = new \Symfony\Component\Serializer\Encoder\JsonEncoder();
$normalizer->setCircularReferenceHandler(function($obj){
return $obj->getId();
});
$serializer = new \Symfony\Component\Serializer\Serializer(array($normalizer), array($encoder));
$qb = $tnkbRepo->createQueryBuilder('c')
->setMaxResults(1);
//$query = $qb->getQuery(); // [1] <<-- this line produce proper YYYY-MM-DD format
//$query = $qb->select('c.expire')->getQuery(); // [2] <<-- this (manual select) line produce DateTime object.
$results = $query->getResult();
return $serializer->serialize($results, 'json');
});
With the first [1] line uncommented I got the proper output I wanted:
[more json output here]...,"expire":"1970-10-25",...
But with the second [2] line uncommented (I intendedly omitted other fields for testing) I got the following output, which wasn't what I expected:
[{"expire":{"timezone":{"name":"UTC","location":{"country_code":"??","latitude":0,"longitude":0,"comments":""}},"offset":0,"timestamp":25660800}}]
I also noticed, with the [2] line Doctrine seems to ignore my entity's getter method (I tried returning empty string). I expect the output will be the same as the [1] case, it makes me curious. My questions are:
How do I achieve the same proper YYYY-MM-DD format with the [2] version?
And why are they produce different output format?
Thank you.
UPDATE
More simplified /debug controller for testing (no serialization):
$app->get('/debug', function() use ($app) {
$tnkbRepo = $app['orm.em']->getRepository('\App\Entity\Tnkb');
$qb = $tnkbRepo->createQueryBuilder('c');
// [1a] normal query. doesn't return Entity, getExpire() isn't called.
/*$query = $qb->select('c.expire')
->setMaxResults(1)->getQuery();*/
// [2a] partial query. returns Entity, getExpire() called.
/*$query = $qb->select('partial c.{id,expire}')
->setMaxResults(1)->getQuery();*/
$results = $query->getResult();
var_dump($results);die;
});
Updated Entity method getExpire():
// src/App/Entity/Tnkb.php (simplified)
// 'expire' field
/**
* #ORM\Column(type="date")
*/
protected $expire;
protected $dateAsString = true;
protected $dateFormat = 'Y-m-d';
// getter
public function getExpire()
{
return ($this->expire instanceof \DateTime) ? $this->dateOutput($this->expire)
: $this->dateOutput(new \DateTime());
}
protected function dateOutput(\DateTime $date) {
if ($this->dateAsString) {
return $date->format($this->dateFormat);
}
return $date;
}
Controller dump results:
[1a] normal query:
// non-entity
array(1) { [0]=> array(1) { ["expire"]=> object(DateTime)#354 (3) { ["date"]=> string(26) "1970-10-25 00:00:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } } }
[2a] partial object query:
// array of entity
array(1) { [0]=> object(App\Entity\Tnkb)#353 (23) { /* more properties */...["expire":protected]=> object(DateTime).../* more properties */
I found out this is normal behaviour with Doctrine, it has something to do with Partial Objects. See my comment below. Link: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/partial-objects.html
I don't think it's good practice to return a \DateTime sometimes but a formatted string other times. However maybe you have your reasons for doing this.
The only reason I can think of the difference in results is if Doctrine calls getters on the properties when loading an entity. I tested with a simple class which has the same expire property and getter. Returning the class still had the serialized (not formatted) \DateTime object, which would suggest that at some point your getter is being called and the property set to a new \DateTime.
My recommendation is to look at the DateTimeNormalizer provided by Symfony in 3.1. If you can't upgrade to 3.1 then you can easily build your own one. Then you can be sure you'll always have consistent \DateTime format in all your responses. You can all remove the ->format(...) from your getter then and always return a \DateTime object. I think this is a much cleaner approach.

Unite Test: Serialization of 'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed

I'm trying to write white test to test my API with file uploads.
I'm following the docs about this using basic client request, not crawler.
The unit test is:
class RecordsControllerTest extends WebTestCase {
private $client;
public function __construct() {
parent::__construct();
$this->client = self::createClient();
$this->client->insulate();
}
public function testApiPostUpload($params){
$fileToUpload = realpath(__DIR__.'/../../resources/mpthreetest.mp3');
$file = new UploadedFile(
$fileToUpload,
'mpthreetest.mp3',
MimeTypeGuesser::getInstance()->guess($fileToUpload),
filesize($fileToUpload)
);
$this->client->request('POST', '/records/'.$params['createdRecordId'].'/upload', array(), array('file' => $file) );
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
}
}
When I execute the test I receive an error:
Exception: Serialization of 'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed
/path/to/project/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php:165
/path/to/project/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php:348
/path/to/project/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php:143
/path/to/project/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php:313
/path/to/project/src/Bundle/Tests/Functional/Controller/RecordsControllerTest.php:182
I have found this question for about the same error, but in this case the request is not sent to the controller and the problem is not the entity and implementing serialization.
Anyone who knows how to fix this?
Anyone who managed to make unit test for uploading file in symfony 2?
You could try to NOT insulate the requests passing false as argument to the insulate method so try this:
$this->client->insulate(false);
instead of this:
$this->client->insulate();
Hope this help
I was able to resolve it by setting the changeHistory parameter to false (7th and last parameter in the request method signature):
$crawler = $client->request($form->getMethod(), $form->getUri(), $values, $files, [], null, false);
This will prevent the serialize on following lines :
if ($this->followRedirects && $this->redirect) {
$this->redirects[serialize($this->history->current())] = true;
return $this->crawler = $this->followRedirect();
}

Testing network which uses AFNetworking with OCMock

I have a class Network that contain next method:
- (void)fetchRecords {
Network * __weak weakSelf = self;
[self.sessionManager POST:#"" parameters:#{#"parameter":"param1"}
success:^(NSURLSessionDataTask *task, id responseObject) {
[weakSelf setRecordsWithLists:responseObject];
}
failure:nil];
}
Property sessionManager is AFHTTPSessionManager class.
I want to test my network communication. I want to check that if success block executed then invoked setRecordsWithLists: method.
I use OCMock, I googled and write next test code:
- (void)tesNetworkSuccessExecuteSetRecords {
id partiallyMockedSessionManager = [OCMockObject partialMockForObject:self.network.sessionManager];
[[partiallyMockedSessionManager expect] POST:OCMOCK_ANY parameters:OCMOCK_ANY success:OCMOCK_ANY failure:OCMOCK_ANY];
[[[partiallyMockedSessionManager expect] andDo:^(NSInvocation *invocation) {
void (^successBlock)(NSURLSessionDataTask *task, id responseObject);
[invocation getArgument:&successBlock atIndex:4];
successBlock(nil, #[#"first", #"second"]);
}] POST:OCMOCK_ANY parameters:OCMOCK_ANY success:OCMOCK_ANY failure:OCMOCK_ANY];
self.network.sessionManager = partiallyMockedAuthorizationSessionManager;
[self.network fetchRecords];
}
Please, tell how to return my own response for success block, if do it wrong. And how to verify that setRecordsWithLists: called from success block.
Well, you are actually setting the expectation twice, which would mean you are expecting the method to be called twice too. If that is not what you intend, you should delete the first expectation.
On the other hand, since you need to check if your method is called, you probably want to create a partial mock for your self.network object. With the partial mock (or spy), you can set the expectation for your method, with any argument you prefer, and verify it to complete the test.
Your code finally should look like this:
- (void)tesNetworkSuccessExecuteSetRecords {
id partiallyMockedSessionManager = [OCMockObject partialMockForObject:self.network.sessionManager];
[[[partiallyMockedSessionManager expect] andDo:^(NSInvocation *invocation) {
void (^successBlock)(NSURLSessionDataTask *task, id responseObject);
[invocation getArgument:&successBlock atIndex:4];
successBlock(nil, #[#"first", #"second"]);
}] POST:OCMOCK_ANY parameters:OCMOCK_ANY success:OCMOCK_ANY failure:OCMOCK_ANY];
id spyNetwork = OCMPartialMock(self.network);
[[spy expect] setRecordsWithLists:OCMOCK_ANY];
self.network.sessionManager = partiallyMockedAuthorizationSessionManager;
[self.network fetchRecords];
[spyNetwork verify];
}
Again, you may add the argument of your preference in the expectation. Beware of the name of the method, it should begin with 'test', otherwise it may not be run or recognized as a test by XCode.