we're working on PHPunitizing our application with two scopes in mind: reduce regressions and bugs in releases.
The application apply an MVC pattern but with no Open Source framework: we're actually use a proprietary one with lots of components coming from symfony (goal is to port all to symfony sometime in the future).
Application has some design flaws so doesn't fit perfectly for Unit Testing. Anyway we've got a point where we can start and here comes some strage behaviours i can't solve.
All of our business methods rely on router object passed by Dependency injection at creation time.
Router exposes some methods to get and set variables like _SERVER, _GET, _POST that are cloned inside router itself (Use of global variable is prohibited by design).
In the following test i set SERVER and GET just before tested class instancing.. so i was expect to find their values correctly during test.
Instead they are fully empty and, of course, test fail.
Any clue of what i'm doing wrong?
Thanks.
/**
* Test external class with valid UUID
*
*/
public function testGetClassPermissionExternal() {
$TEST_SERVER_DATA = array(
'REQUEST_URI' => 'https://sviluppo.pardgroup.com/test',
'SERVER_PORT' => 443,
'SERVER_NAME' => 'sviluppo.pardgroup.com'
);
// Due to bad design superglobal $_SERVER need to be fullfilled.
$_SERVER = $TEST_SERVER_DATA;
$this->router->setSERVER($TEST_SERVER_DATA);
$TEST_POST_DATA = array('uuid' => self::$UUID);
$this->router->setGET($TEST_POST_DATA);
$this->class = \permission::getInstace($this->router);
$test_class = array("permissionTest/external");
$this->assertFalse($this->invokeMethod($this->class, "getClassPermission", $test_class));
}
Related
I'm putting an object into the session, and then in a latter step in the scenario I need to use properties of that object in an http request.
The Gatling expression language does not support accessing properties of an object, so I thought I could extract the object from the session manually and then extract the properties I needed in the http request using the following code.
exec(session => {
val project = session("item").as[Project]
println(s"name = ${project.getName}, daysToComplete = ${project.getDaysToComplete}")
http("Health Check")
.get(s"/health")
.queryParam("name", s"${project.getName}")
session
})
But structured this way the http request is not added into the chain and so does not execute.
Is there anyway to do this, short of putting the individual properties into the session. This is a simplified example. The object I'm putting into the session is much more complicated than this.
Already answered on Gatling's official mailing list.
This cannot work, please read the documentation: https://gatling.io/docs/gatling/reference/current/general/scenario/#exec
Gatling DSL components are immutable ActionBuilder(s) that have to be chained altogether and are only built once on startup. The result is a workflow chain of Action(s). These builders don’t do anything by themselves, they don’t trigger any side effect, they are just definitions. As a result, creating such DSL components at runtime in functions is completely meaningless. If you want conditional paths in your execution flow, use the proper DSL components (doIf, randomSwitch, etc)
exec { session =>
if (someSessionBasedCondition(session)) {
// just create a builder that is immediately discarded, hence doesn't do anything
// you should be using a doIf here
http("Get Homepage").get("http://github.com/gatling/gatling")
}
session
}
You should do something like:
foreach(components, "component") {
exec(
http { session =>
val component = session("component").as[ITestComponent]
s"Upload Component ${component.getId}"
}.post { session =>
val component = session("component").as[ITestComponent]
s"/component/$repoId/$assetId/${component.getId}/${component.getResourceVersionId}"
}
.bodyPart(RawFileBodyPart("resource", session => {
val component = session("component").as[ITestComponent]
component.getContent.getAbsolutePath()).contentType(component.getMediaType()).fileName(component.getContent.getName())).asMultipartForm
}
)
}
Yes, this is pretty complicated. The reason it looks so over bloated is because you're trying to use a Java POJO (hidden behind an interface), instead of using Scala case classes.
If you were to use a Scala case class, you could use Gatling Expression Language (it doesn't support accessing POJOs by reflection atm) and do something like this:
foreach(components, "component") {
exec(
http("Upload Component ${component.id}")
.post(s"/component/$repoId/$assetId/$${component.id}/$${component.resourceVersionId}")
.bodyPart(
RawFileBodyPart("resource", "${component.content.absolutePath}")
.contentType("${component.content.mediaType}")
.fileName("${component.content.name}")
).asMultipartForm
)
}
I'm writing functional / controller tests for a ZF3 application (driven by PHPUnit and zendframework/zend-test). Like this:
public function testWhatEver()
{
$this->dispatch('/');
$this->assertResponseStatusCode(Response::STATUS_CODE_200);
}
It's working pretty well. But now I got a case, where I need to test the application with multiple mutually exclusive configs.
E.g., the case "authentication": The application provides multiple authentication methods (let's say: AuthA, AuthB,AuthC). (That is configurable via setting of the auth.type's value in the config file.) I want to test each of them. That means, it's not enough to have special test configs in the /config/autoload/test/*{local|global}.php. I need to be able to manipulate them for every test (before I call the dispatch(...) method).
How to manipulate the application configs for / from controller tests (on the fly)?
If no better solution can be found, a possible workaround might be to edit the config file (by using file_put_contents(...) or something like this) before every test. But it's a bit ugly (and slow).
In general I see no really nice solution for this problem. But there some more or less acceptable workaround:
Workaround 1: manipulating the according config file for every test
$configs = file_get_contents(...)
searchByRegexAndManipulateConfigs(...)
file_put_contents(...)
It's much effort and would make the testing slower (due to reading from / writing to the filesystem).
Workaround 2: simple files with only one config value
We can create files like config.auth.type.php or config.auth.type.txt (one per config value t keep the file really simple) and to use inclue or file_get_contents(...) call as value in the config. The the value in the file needs to be manipulated before the test execution.
It's a bit less effort (we don't need to write complex RegEx), but might make the test considerably slower, since every application request would start by reading an additional file.
Workaround 3: passing configs values through GLOBALS
It's the simplest and fastest variant. We just save the needed value into a global variable and read it in the config (file's) array. After the test we remove the variable:
AuthBTest
...
protected function setUp() // or setUpBeforeClass()
{
parent::setUp();
$GLOBALS['appTestConfigs']['auth.type'] = 'AuthA';
}
protected function tearDown() // or tearDownAfterClass()
{
parent::tearDown();
unset($GLOBALS['appTestConfigs']);
}
...
/config/autoload/test/local.php
return [
'auth' => [
'type' => isset($GLOBALS['appTestConfigs']['auth.type']) ? $GLOBALS['appTestConfigs']['auth.type'] : 'AuthA',
],
];
I'm using PHPUnit with processIsolation="true" because I need to set cookie value in the code I'm testing, and without processIsolation="true" it can't be done. But in one of my test case, I'm getting the error:
PHPUnit_Framework_Exception: [10-May-2018 15:23:28 UTC] PHP Fatal error: Uncaught PDOException: You cannot serialize or unserialize PDO instances in -:394
Stack trace:
#0 [internal function]: PDO->__sleep()
#1 -(394): serialize(Array)
#2 -(536): __phpunit_run_isolated_test()
#3 {main}
thrown in - on line 394
Test case:
public function testUserCookieIsSavedToClicksTable()
{
$cookie = sha1('12345');
$_COOKIE['user_cookie'] = $cookie;
$offerId = 1;
$tx = $this->getTx($offerId);
// Go
$this->controller = new ClickController(new Click(new Database(Cache::getInstance()), new Queue, new RedisStorage()));
$this->runLocal([1, $this->userId], null);
$sql = 'select * from clicks order by id desc limit 1';
$click = $this->db->query($sql, [], 'fetch');
$this->assertEquals($tx, $click['tx_id']);
$this->assertEquals($click['user_cookie'], $cookie);
}
This error is thrown when calling any of $this->assertEquals($tx, $click['tx_id']); or $this->assertEquals($click['user_cookie'], $cookie);. But I can var_dump any variables used in those assertions.
I tried all solutions from here but they not working for me
Process Isolation require that all objects in the process to isolate can be serialized / unserialized.
The error you see is about a specific object that does not support PHP object serialization / unserialization.
That first of all means in the case you ask about, that you can not make use of process isolation for that test.
If you chew on that a bit and change some thoughts about what is going on here and why some objects (like database connections) normally can not be serialized and then as well, why process isolation might make use of serialization as well, it perhaps comes to mind as well why process isolation for database connections is not such a good idea after all.
So even though you see a technical limitation here that is causing the error, in the end I'd say it makes perfect sense to not run database connection based tests in isolated processes as part of the same testsuite.
This is perhaps not the answer you hoped for.
Perhaps divide and conquer can make sense here: Put those tests for which process isolation work into a testsuite of it's own and configure process isolation for it.
For the other one do the same into a testsuite of their own as well.
The PHPUnit XML configuration file offers settings to make these, sometimes it is even needed to have multiple of these XML configuration files, depends a bit on the Phpunit version used, but also for configuration settings for the test-runner.
This is probably error in your db component. It should implement __sleep() method to make sure, that PDO instance is not serialized with it (and probably implement __wakeup() to reconnect after deserialization).
You didn't share any informaction about db component, but this could be achieved by something like this:
public function __sleep() {
$fields = (array) $this;
unset($fields['pdo']);
return array_keys($fields);
}
I am trying to refactor an older codebase to use Silex - my thinking was that I could have Silex handle all actions it understands - and defer everything it doesn't to the legacy controller architecture:
$app = new Silex\Application();
$app['debug'] = true;
// TODO: Refactor old actions into Silex incrementally
//$app->get('/hello/{name}', function ($name) use ($app) {
// return 'Hello ' . $app->escape($name);
//});
// NOTE: Defer handling to legacy controllers:actions
$app->error(function (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e, $code) {
echo 'Silex not provided URL - default to legacy';
try {
$response = new Spectra_Application_Environment_Http_Response();
$request = new Spectra_Application_Environment_Http_Request();
$router = new Spectra_Application_Controller_Router_Adapter(
$_SERVER['PATH_INFO'], $request, new Common_Controller_Router($GLOBALS['AQUARIUS']['ROUTES'], key($GLOBALS['AQUARIUS']['ROUTES']))
);
$front = Spectra_Application_Controller_Front::getInstance();
$front->setResponseObject($response)->setRequestObject($request);
$front->addPrePluginFilter(new Common_Controller_Filter_ActionCheck());
echo cadorath_main($router, $front)->sendResponse();
}
catch (Exception $e) {
$result['success'] = false;
$result['errors']['reason'] = $e->getMessage();
header('Content-Type: application/json');
echo json_encode($result);
}
});
$app->run();
This isn't working. What am I not understanding of how Silex exception handling works?
Sorry for this, but before I give you an answer I feel I have to point out a few things and this is too long for the comment field. Please take them as a friendly advice and not as a criticism.
First, Silex is being discontinued very soon. Since you are just starting out with migrating the codebase I suggest either a different microkernel-framework like SlimPHP or Lumen or switching to Symfony 4 using Flex. The latter is probably closest to the experience you get with Silex as it uses the same components it just gives you an easier path to extend your application with features from Symfony without having to manually wire everything up using Silex' provider logic.
The second point I want to make is your use of echo: It might be in there for debugging purposes, but if not, please be aware that this "breaks" most frameworks as they usually have some event system that is triggered during the runtime and just printing out stuff bypasses and in some cases even breaks those. When you are using Silex/Symfony you should use the Request and Response objects to handle the incoming and outgoing data and use return if you want to pass them on. So in your case instead of doing
header('Content-Type: application/json');
echo json_encode($result);
You could do:
return new JsonResponse($result);
When you use the JsonResponse object it will set the headers for you and also do the JSON-encoding. So it's even easier and shorter than before.
As to your actual question, please provide more information on the errors you get. Maybe check your application or server logs (apache, nginx, ...).
My initial assumption is that the Request you are using for your legacy application does not contain all the necessary information. You might have to transfer over some data from Symfony's Request object or "fix" some of the values as they point to your new application, not the legacy application (e.g. SCRIPT_PATH) which might mess up some assumptions in your old code. Generally speaking try to identify which parts of the legacy code base use globals and check if those values are correctly set or refactor your application to not require those globals but instead read them from a config file for example.
i am unit testing in laravel with Phpunit. The situation is i have to return a model instance from the controller back to the testing class. There i will use the attributes of that object to test an assertion. How can i achieve that?
Currently i am json encoding that instance into the response. And using it in a way that works but is ugly. Need a clearer way.
This is my test class:
/** #test
*/
function authenticated_user_can_create_thread()
{
//Given an authenticated user
$this->actingAs(factory('App\User')->create());
//and a thread
$thread = factory('App\Thread')->make();
//when user submits a form to create a thread
$created_thread = $this->post(route('thread.create'),$thread->toArray());
//the thread can be seen
$this->get(route('threads.show',['channel'=>$created_thread->original->channel->slug,'thread'=>$created_thread->original->id]))
->assertSee($thread->body);
}
and this is the controller method:
public function store(Request $request)
{
$thread = Thread::create([
'user_id'=>auth()->id(),
'title'=>$request->title,
'body'=>$request->body,
'channel_id'=>$request->channel_id,
]);
if(app()->environment() === 'testing')
{
return response()->json($thread); //if request is coming from phpunit/test environment then send back the creted thread object as part of json response
}
else
return redirect()->route('threads.show',['channel'=>$thread->channel->slug,'thread'=>$thread->id]);
}
As you can see in the test class, i am receiving the object returned from controller in the $created_thread variable. However, controller is returning an instance of Illuminate\Foundation\Testing\TestResponse, so the THREAD that is embedded in this response is not easy to extract. You can see i am doing
--> $created_thread->original->channel->slug,'thread'=>$created_thread->original->id]. But i am sure there is a better way of achieving the same thing.
Can anyone please guide me to the right direction?
PHPUnit is a unit testing suite, hence the name. Unit testing is, by
definition, writing tests for each unit -- that is, each class, each
method -- as separately as possible from every other part of the
system. Each thing users could use, you want to try to test that it --
and only it, apart from everything else -- functions as specified.
Your problem is, there is nothing to test. You haven't created any method with logic which could be tested. Testing controllers action is pointless, as it only proves that controllers are working, which is a Laravel creators thing to check.