jeudi 27 juillet 2017

PHP: How to test an ORM against multiple database systems

If one is going to build an ORM in PHP by himself, how would one properly test it against multiple database systems?

I know, this question requires a long answer... I ask that because my team will need to implement one by itself and I am very curious.

So I am thinking of an ORM that provides multiple database drivers (like Doctrine):

  1. MySQL
  2. PostgreSQL
  3. Informix
  4. ...

Each driver should provide a querybuilder who generates Queries that are parsable by its underlying database system.

As that is, i think an ORM should always test all capabilities (associations, inheritance, what else, ..) with all possible (or currently implemented) drivers.

I already took a look how Doctrine does that with PHPUnit, but it seems they don't test their ORM like that... see here: phpunit.xml.dist on github

Well, of course they test their ORM against multiple database systems, but apparently not automatically..!?

So how would such an automatic "testsuite" look like, that tests against multiple database systems?


One Example

Using PHPUnit (and DBUnit indeed) I can currently only think of something like this...

phpunit.xml would look this:

<phpunit bootstrap="buildUpDBConnections.php">
....
</phpunit>

So the phpunit.xml does not provide any db settings, but executes a script (buildUpDBConnections.php), that sets up all database connections.

I think this connections then must somehow be publicly available as a collection, which is injected in all tests that should execute queries.

And before running the tests, this script prepares the fixtures for each database.

And then each test runs its queries against all connections. so a test is only successful, if the queries were executed correctly for all database systems.

Is this "concept" actually correct? are there more practical ways?

Can't enable codeception tests for Yii2

I have problems configuring Codeception acceptance tests for my Yii2 website.

Tests are running okay, but I'd like to change the configuration so testing contact form won't send any emails. It should be pretty simple task, but I can't force it to use alternative config.

Following this tutorial: http://ift.tt/2dCMwYZ

/codeception.yml:

actor: Tester
paths:
    tests: tests
    log: tests/_output
    data: tests/_data
    helpers: tests/_support
settings:
    bootstrap: _bootstrap.php
    memory_limit: 1024M
    colors: true
modules:
    config:
        Yii2:
            configFile: 'config/test.php'

/tests/acceptance.suite.yml

class_name: AcceptanceTester
modules:
    enabled:
        - WebDriver:
            url: http://localhost/mypage
            browser: firefox
        - Yii2:
            part: orm
            entryScript: index.php
            cleanup: false
            configFile: test.php

And again I have no idea which directory this file points to. I tried with /config/test.php, /tests/config/test.php, nothing.

/config/test.php

<?php
$params = require(__DIR__ . '/params.php');
$dbParams = require(__DIR__ . '/test_db.php');

/**
 * Application configuration shared by all test types
 */
return [
    'id' => 'basic-tests',
    'basePath' => dirname(__DIR__),    
    'language' => 'en-US',
    'components' => [
        'db' => $dbParams,
        'mailer' => [
            'useFileTransport' => true, // <--- I'm interested in this setting 
        ],
        'urlManager' => [
            'showScriptName' => true,
        ],
        'user' => [
            'identityClass' => 'app\models\User',
        ],        
        'request' => [
            'cookieValidationKey' => 'test',
            'enableCsrfValidation' => false,
            // but if you absolutely need it set cookie domain to localhost
            /*
            'csrfCookie' => [
                'domain' => 'localhost',
            ],
            */
        ],        
    ],
    'params' => $params,
];

I even thought that this path might be relative to /tests directory, so I created file there as well:

/tests/config/test.php

<?php
// config/test.php
$config =  yii\helpers\ArrayHelper::merge(
    require(__DIR__ . '/main.php'),
    require(__DIR__ . '/main-local.php'),
    [
        'id' => 'app-tests',
        'components' => [
            'db' => [
                'dsn' => 'mysql:host=localhost;dbname=yii_app_test',
            ],
            'mailer' => [
                'useFileTransport' => true
            ]
        ]
    ]
);
return $config;

/tests/_bootstrap.php

<?php
define('YII_ENV', 'test');
defined('YII_DEBUG') or define('YII_DEBUG', true);

require_once(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
require __DIR__ .'/../vendor/autoload.php';

part of /config/web.php to check if environment values are passed, but they aren't

if (YII_ENV_TEST || YII_ENV === 'test'){
    die('Testing settings loaded');
}

Launching my tests with ./vendor/bin/codecept run tests/acceptance/ContactCest.php

I'm not publishing tests because they are irrelevant, as they just use clicks on elements to send contact form. At the moment I've wasted 4 hours trying to override that config file. Probably it's some stupid rookie mistake, but I'm about to give up now.

Help me, StackOverflow, you are my only hope! ;)

NoMethodError Ruby MiniTest

I am trying to get started with Minitest. I get the following error:

Please Enter your Weight in lbs: 250
Please Enter your Height in cm: 190
You have a BMI of 31.41 
bmicalc_test.rb:4:in `<main>': private method `test' called for 
MiniTest:Module (NoMethodError)

This is my main code file:

class BmiCalc

    def initialize(weight, height)
        @weight = weight;
        @height = height;
    end

    def bmi
        kgweight = @weight * 0.4535;
        meterheight = @height * 0.01;
        bmivalue = kgweight/(meterheight * meterheight);
        return bmivalue;
    end

end

print "Please Enter your Weight in lbs: ";
weight = gets.to_f;

print "Please Enter your Height in cm: ";
height = gets.to_f;

bmiCalc = BmiCalc.new(weight, height);
answer = bmiCalc.bmi;

printf("You have a BMI of #{'%.2f' % answer} \n");

This is my MiniTest file:

require 'minitest/autorun'
require_relative "bmicalc.rb"

class TestBmi < Minitest::test

    def setup
        @bmicalc = BmiCalc.new(190, 250);
    end

    def test_answers
        assert_equal 31.40581717451523, @bmicalc.bmi;
    end

end

Firstly, I don't understand why it asks for input from me when I call this in the test file. I am instantiation a sample class already so why does it ask for input.

Secondly, I am not sure I understand the error or the nature of it. I am trying to follow this tutorial: http://ift.tt/1APaSmR

How to share setup method across multiple tests

I have a pretty big setup() method that sets up a lot of conditions that I need to test across two of my specs. Instead of copy pasting the same setup method in two different specs, I would like the same setup() method to be shared between the two specs.

I noticed that Spock offers setupSpec() method. However, this isn't working for me. It is giving me error: Method on class [com.somepackage.Keyword] was used outside of a Grails application.

Below is an example of what I have:

@TestFor(MyService)
@TestMixin(DomainClassUnitTestMixin)
@Mock([Keyword])
class MyServiceSpec extends Specification {
  def setup() {
     new Keyword(name: "test").save()
  }
}

The above works, however, if I change to setupSpec() then it fails with error Method on class [com.somepackage.Keyword] was used outside of a Grails application.

@TestFor(MyService)
@TestMixin(DomainClassUnitTestMixin)
@Mock([Keyword])
class MyServiceSpec extends Specification {
  def setupSpec() {
     new Keyword(name: "test").save()
  }
}

Question

How can I share the same setup method in MyServiceSpec across multiple specs. I run the test with grails test-app -echoOut -unit MyService

Karate vs Spock

I've recently found Karate framework for testing Web Services. But there is also Spock framework providing similar (to my mind) functionality. What are the differences between the frameworks? I would like to suggest our testers to take a look at it.

How to test if the data entered through registration page of a web application is stored into database or not with Selenium?

I am new to Selenium. I am testing a user registration form in which i have to verify if the data is inserted into database or not. I am using following code to check. Is there any better option than this?

@Test
public void testVerifyListOfRecords() throws InterruptedException {

  String query = "SELECT ID FROM usermaster WHERE RegistrationNumber = '" + REGNO + "' ";
  String ID = DatabaseConnector.executeSQLQuery("QA", query);

  if(ID == ""){
    STATUS = "FALSE";
    System.out.println("REGISTRATION NUMBER IS NOT INSERTED..!! TEST FAILED..!!");
  } else STATUS = "TRUE";

  String sqlQuery = "SELECT FirstName,LastName,SpecialityCode,QualificationCode FROM usermaster WHERE ID = '" + ID + "' ";
  List<String> listOfDBValues = new ArrayList<String>();
  listOfDBValues = DatabaseConnector.executeSQLQuery_List("QA", sqlQuery);
  List<String> Branch = new ArrayList<>();
  Branch.add(listOfDBValues.get(0));

  List<String> list1 = new ArrayList<>(Arrays.asList("testfirstname testlastname ORP DM"));
  Assert.assertEquals(STATUS, "TRUE");
  System.out.println("STATUS IS " + STATUS);
  Assert.assertEquals(Branch, list1);
}

Testing Urbanairship push notifications on android emulator

I would like to test push notifications sent by Urbanairship on android emulators but I could not find any image/configuration where it would actually working. I installed 1st flight demo app provided by UA and connected with my UA account on all emulators I tried so far.

  1. I tried to use created virtual device by Android Studio, for emulators with google play I was able to install 1st flight app but sent notifications via web weren't displayed.
  2. For created emulators with google API I wasn't able to even install 1st flight app which was marked on google play web as already installed but I could not find on emulator.
  3. For used Android 6 r3 image on Virtualbox I was able to install app but it was working as described in 1st point - notification is marked as sent but nothing was displayed on device.
  4. For used Andy default emulator the same as in 1/3 points.
  5. I added manually play store in Android Studio to Android 6 with google API, but I was not able to successfully log in there.

For real devices I was able to display sent notification on Android 6, for Android 7 it was actually working as for emulators.

I don't know what more specific emulators / images can I use, what else could be changed, added or configured to make it working. Any suggestion is welcome.