Spock's #IgnoreIf Closure doesn't see System prop variable, set in jUnit's Suite - automated-tests

I Use Geb + Spock + jUnit runner + Maven
My Specs are like:
#Stepwise
class My_Spec extends GebReportingSpec {
#IgnoreIf({properties['sss'].contains('true')})
def "testFeature 1" (){
println ("--> Feature 1 runs")
given:
println ("--> mySystemProp is: ${properties['sss']}")
...
when:
...
then:
...
}
def "testFeature 2" (){
println ("--> Feature 2 runs")
when:
...
then:
...
}
}
I need to run my Specs with jUnit runner because i need to group it in TestSuites. I Found a way to set System property before testSuite run. It available in jUnit 4.9 - #ClassRule. So, i use it here.
By this way my TestSuites are like:
#RunWith(Suite.class)
#Suite.SuiteClasses([
My_Spec.class,
My_Spec1.class,
My_Spec2.class
])
class TestSuite extends Specification {
#ClassRule
public static ExternalResource testRule = new ExternalResource(){
#Override
public void before() throws Throwable{
System.setProperty('sss', 'true')
}
}
}
But #IgnoreIf behaviur doesn't work: it doesn't see added system property 'sss' however, in feature method this property is available:
when feature runs, it gives next output:
Running TestSuite
--> Feature 1 runs
--> mySystemProp is: true
--> Feature 2 runs
All this i run with maven install.
Piece of my pom.xml:
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<includes>
<include>TestSuite.*</include>
</includes>
<systemPropertyVariables>
What am i doing wrong?
If all is correct - How can i make it work with spock's #IgnoreIf and props, that i need to define in jUnit TestSuite?
( Please, do not offer to use jUnit's #Categories. )
Thanks.

Are you sure that properties['sss'] resolves to the right method call from within the closure passed to #IgnoreIf? I have been bitten by trying to be too groovy in the past, especially trying to use this kind of concise expressions with static imports when resolving system props in particular. Have you tried changing it to System.getProperty("sss")?
Also, the closure passed to #IfIgnore has a delegate set to org.spockframework.runtime.extension.builtin.PreconditionContext which has sys property, so you could try sys["sss"] instead. If that doesn't help then you can debug the properties available in the closure by changing your code to:
#IgnoreIf({ println sys; sys["sss"].contains("true") })

Related

Container parameter not available/injected in Symfony 5 service when runnig PHPUnit tests

While creating some (functional) PHPUnit tests for my Symfony 5 project, I ran into the problem, that container parameters are not properly injected into services during the tests:
For example the following parameters are available an used in the project:
// config/packages/some_bundle.yaml
some_bundle:
api:
key: 'abc' // default value is null
mode: 'live' // default value is 'sandbox'
// SomeBundle/config/services.yaml
services:
_defaults:
autowire: true
autoconfigure: true
bind:
$apiMode: '%some_bundle.api.mode%'
$apiKey: '%some_bundle.api.key%'
// SomeBundle/src/Service/ApiService.php
class ApiService {
public function __construct($apiMode, $apiKey) {
// log mode and key ...
}
}
// Checking that parameters are available
$ php bin/console debug:container --parameters --env=test
Symfony Container Parameters
============================
-------------------------------------------
Parameter Value
-------------------------------------------
...
some_bundle.api.key abc
some_bundle.api.mode live
The problem:
I would assume that when using these parameters during tests, that their values are abc and live. However, logging shows, that the ApiService is created with the default values null and sandbox instead when running the tests.
The testCase extends KernelTestCase and the kernel is booted. Getting the parameter directly from the test container also shows the wrong value:
// Within the test case
$mode = self::getContainer()->getParameter('some_bundle.api.mode');
$this->assertEquals($mode, 'live', 'error');
// test output
Failed asserting that two strings are equal.
--- Expected
+++ Actual
## ##
-'live'
+'sandbox'
When using the project directly in browser, everything is fine.
The dev, prod and test environment all use the same config files, there a no special env.test files.
The phpunit.xml.dist file is setup to use the test environment, but using other environments here does not make any difference.
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ...>
<php>
<server name="APP_ENV" value="test" force="true" />
...
How to solve this? Are container parameters skipped completly when running tests and only default values can be used? Or is there anything wrong with my config?
I can't really see anything wrong with the way you have done it and would have thought it would work, although I wouldn't have injected the parameters like that. In my bundle in the DependencyInjection folder I would pass the params in the extension file instead of binding them in the services.yaml.
public function load(array $configs, ContainerBuilder $container): void
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yaml');
$definition = $container->getDefinition('SomeBundle/src/Service/ApiService');
$definition->addArgument($configs['api']['mode']);
$definition->addArgument($configs['api']['key']);
}

Silverstripe 4 SapphireTest class can't be found

I've upgraded from SilverStripe 3 to 4 and now my phpUnit tests own't run because they can't find any of my Custom Classes.
There must be something missing from an autoloader or something.
I have a simple test like this
use SilverStripe\Dev\SapphireTest;
class EntityTest extends SapphireTest
{
var $Entity;
function setUp()/* The :void return type declaration that should be here would cause a BC issue */
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->Entity = new \My\API\Client\Model\Entity();
}
function testMethods(){
$this->assertMethodExist($this->Entity,'setName');
}
function assertMethodExist($class, $method) {
$oReflectionClass = new ReflectionClass($class);
assertThat("method exist", true, $oReflectionClass->hasMethod($method));
}
}
and when running I get:
$ php vendor/phpunit/phpunit/phpunit mysite/tests/EntityTest.php
Fatal error: Class 'SilverStripe\Dev\SapphireTest' not found
I ran into a similar issue with SilverStripe 4.1, here is what I found (and resolved).
1) As of 4.1, you need to use --prefer-source instead of --prefer-dist to get the test code. Test code is now omitted from the distributed packages, see https://github.com/silverstripe/silverstripe-framework/issues/7845
2) phpunit must be in require-dev at version ^ 5.7 - I had a different value and this was the cause of the autoload issue.
I've created a test module for reference, see https://github.com/gordonbanderson/travistestmodule
Cheers
Gordon
You're probably missing the test bootstrapping. SS4 still relies on the SilverStripe class manifest to register available classes (not just PSR-4 autoloaders), so you need to include it. Try either of these:
$ vendor/bin/phpunit --bootstrap vendor/silverstripe/framework/tests/bootstrap.php mysite/tests
or create a phpunit.xml file in your root project:
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
</phpunit>
You may also use the equivalent file from the CMS module instead, but you probably won't see any differences until you start to integrate your testsuite into a CI provider.

Override a symfony service tag with a compiler pass

I'm trying to override a tag in a symfony service definition with a compiler pass. The service as an example would be data_collector.translation.
The goal is to deactivate the data collector service to disable the element in the symfony web developer toolbar. To do this, I have to set the priority of the data_collector tag to 0.
I could also override it in my own service definition:
services:
data_collector.translation:
class: 'Symfony\Component\Translation\DataCollector\TranslationDataCollector'
tags:
- {name: 'data_collector', priority: '0'}
arguments: [#translator.data_collector]
But as I want to do this for a few of the data collectors, I would need to know the mandatory arguments for the data collector definition. The priority works the same for all collectors and therefore I would only need the name of the collector to disable it.
So I wrote the following compiler pass:
class DataCollectorCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('data_collector.translation')) {
return;
}
$definition = $container->getDefinition('data_collector.translation');
$tags = $definition->getTags();
$tags['data_collector'][0]['priority'] = 0;
$definition->setTags($tags);
$container->setDefinition('data_collector.translation', $definition);
}
}
To make things more wired: When I run this command:
$ php app/console container:debug --show-private --tag='data_collector'
I get the following output:
data_collector.translation #WebProfiler/Collector/translation.html.twig translation 0 Symfony\Component\Translation\DataCollector\TranslationDataCollector
So the priority even in the debugger is set to 0.
But for which reason ever the element is still shown in the toolbar.
What did I do wrong here? Is there another mechanism for overwriting a tag within a compiler pass?
The compiler pass does run (tested it with printing out stuff)
I'm using Symfony 2.7.1
Turns out the code does work, the only problem is, that the CompilerPass is run after the ProfilerPass which is part of the FrameworkBundle. Putting my bundle with the CompilerPass before the FrameworkBundle in the AppKernel solves the problem (more information here). For not even initiating the data collectors it's better to remove all tags instead of just setting the priority to 0.
That's what the final solution looks like:
class DataCollectorCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$collectorsToRemove = [
'data_collector.form',
'data_collector.translation',
'data_collector.logger',
'data_collector.ajax',
'data_collector.twig'
];
foreach($collectorsToRemove as $dataCollector) {
if (!$container->hasDefinition($dataCollector)) {
continue;
}
$definition = $container->getDefinition($dataCollector);
$definition->clearTags();
}
}
}
Can you try this?
if (!$container->hasDefinition('data_collector.form')) {
return;
}
$definition = $container->getDefinition('data_collector.form');
$definition->clearTags();
$container->setDefinition('data_collector.form', $definition);
Why not use your compiler pass to manipulate directly the service Definition of the service holding all these collectors ?
If I look at the compiler pass responsible for loading the data collector, it seems that they are all injected using a method call injection.
You could use your compiler pass to rewrite the method call array using methods like setMethodCalls, removeMethodCall, ... of the Definition entity.
The method call manipulation documentation : link

Using modules in Meteor.js with Typescript

Folks, I'm trying to do something that I thought ought to be simple, but I must be doing something wrong. I'm trying to simply have a clear structure in my meteor application which uses Typescript.
Here are my requirements:
All interfaces are available in both client and server
Some class implementations are only available on the server
I don't want to rely on file load order for my application to work properly
I need my own module to not clash with global objects (such as the Position class for example)
I need to have one monolithic include file for server, one for both client and server and one for client (don't want to have 10s of includes on top of my files)
The setup that I have right now is this
server
server-book.ts
client
shared
collections.ts
definitions
server
include.d.ts (includes all .d.ts files in this folder)
server-book.d.ts (server specific implementation of book)
client
shared
include.d.ts (includes all .d.ts files here)
book.d.ts (book interface definition)
collections.d.ts
In each .d.ts file I have
module MyModule {
interface Bla {}
};
In each .ts file that defines a class I have:
module MyModule {
export class MyBla implements Bla {};
}
All .d.ts files generated for classes are generated by tsc -d.
No .ts files are being included via ///<reference> rather only .d.ts files.
Now, when I run this, I get an error that MyModule is undefined:
/// <reference path="shared/include.d.ts"/>
/// <reference path="server/include.d.ts"/>
Meteor.startup(() => {
var temp = new MyModule.ServerBook();
});
The error occurs right on MyModule.
What am I doing wrong? What should be the proper setup here?
Thanks!
I have dealt with this issue on my blog. I decided to use the evil eval command, since it gave me the easiest possibility of using modules till something more sophisticated appears.
File /lib/foo.ts is position in the subdirectory since it has to be loaded before Bar.
eval('var Hugo = (this.Hugo || (this.Hugo = {})'); // this will override the automatically emitted var Hugo and assigns it with globally defined Hugo module
module Hugo {
export class Foo {
foo():string {
return 'foo'
}
}
}
File /bar.ts
/// <reference path="lib/foo.ts"/>
eval('var Hugo = (this.Hugo || (this.Hugo = {})'); // this will override the automatically emitted var Hugo and assigns it with globally defined Hugo module
module Hugo {
export class Bar extends Foo {
bar () : string {
return 'bar';
}
}
}
File /test.ts
/// <reference path="lib/foo.ts"/>
/// <reference path="bar.ts"/>
var m = new Hugo.Bar();
console.log(m.bar());
console.log(m.foo());
As mentioned here, for classes, the solution is even simpler:
class ExportedClass {
variable : int;
}
this.ExportedClass = ExportedClass;
Definition files should use the declare keyword. You would normally get an error if you didn't use this keyword.
declare module MyModule {
export interface Bla {}
}
And
declare module MyModule {
export class MyBla implements Bla {
}
}
It is also worth checking that the ServerBook class has the export keyword (just like MyBla in your examples).
After lot of trial and errors, here are my findings so far :
Using typescript "module" keyword doesn't get well with Meteor. I think at the moment you cannot use it (or the workarounds are too complicated for me).
However, here is what you can do :
Let say that you have package A where you want to define a class ClassToExport which you want to make public.
class ClassToExport {
getFoo(){
return "foo";
}
}
Please note that you can't write this.ClassToExport = ClassToExport and
api.export('ClassToExport') or else ClassToExport won't be available in the global scope of package A, hence the need for a module/namespace for exporting your class, which we will see next.
Now, for the class to be available for the consumers of your package, you have to create a namespace, which will be the equivalent of the "module" typescript keyword for internal module.
So let's write :
declare var packageA; //so that the compiler doesn't complain about undeclared var
packageA = packageA || {}; //so that this namespace can be reused for the entire package
packageA.ClassToExport = ClassToExport; //the actual export
Now, don't forget to write
api.export('packageA') in the package.js of package A
If you have a package B where you want to use ClassToExport, you write in package B:
var cte = new packageA.ClassToExport();
without forgetting to api.use package A in package B's package.js
If you don't want to write the namespace each time you use the class, you can also write var ClassToExport = packageA.ClassToExport; at the top of your using file.
If you need a global class for you package only, without exporting it, then you can do instead just :
this.ClassToExport = ClassToExport
and again don't write api.export('ClassToExport'), or it won't be available in the package anymore.
This way, i think the features (export/ import) of internal typescript modules are there.
If you are not afraid of gulp build, I have prepared a typescript boilerplate project which allows you to comfortably use typescript from within your app, not depending on packages.
https://github.com/tomitrescak/meteor-boilerplate-typescript
Random idea, what about extend Meteor instead of Window.
Meteor.yournamespace = Meteor.yournamespace || {};
Meteor.yournamespace.myclass = new MyClass();
or
Meteor.yournamespace.MyClass = MyClass();
I think this is less invasive than go directly to the window object IMHO. my two cents.
now you can do Meteor.yournamespace.MyClass :P
--EDIT
Then you could create a meteor-extend.d.ts file and do something like:
/// <reference path="main.d.ts" />
declare module Meteor {
var yournamespace: any;
}
Now you can remove the <any> before Meteor and Typescript will not complaint.

ZF2 unit-testing authentication

I was learning about unit testing and I attempted to resolve the following issue:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for zfcUserAuthentication
... using the only answer given at:
Simple ZF2 Unit Tests for a controller using ZfcUser
So my setUp function looks the same. Unfortunately, I get the error message:
Zend\Mvc\Exception\InvalidPluginException: Plugin of type Mock_ZfcUserAuthentication_868bf824 is invalid; must implement Zend\Mvc\Controller\Plugin\PluginInterface
It is caused at this part of the code (split up in my code in the same way):
$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock); // Error refers to this line.
The $authMock object is apparently not implementing plugininterface, which I need to implement to pass into setService.
Is $authMock not meant to be passed there for it's use in unit testing? Should I be using a different (unit-testing oriented) setService method?
I need a way to handle logging into my application, or my unit testing is pointless.
Thanks for any advice.
=== Edit (11/02/2013) ===
I wanted to focus on this part for clarification, as I think this is the problem area:
// Getting mock of authentication object, which is used as a plugin.
$authMock = $this->getMock('ZfcUser\Controller\Plugin\ZfcUserAuthentication');
// Some expectations of the authentication service.
$authMock -> expects($this->any())
-> method('hasIdentity')
-> will($this->returnValue(true));
$authMock -> expects($this->any())
-> method('getIdentity')
-> will($this->returnValue($ZfcUserMock));
// At this point, PluginManager disallows mock being assigned as plugin because
// it will not implement plugin interface, as mentioned.
$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock);
If the mock doesn't handle necessary implementations, how else am I to pretend to login?
You have a problem with name-spacing or your autoloader.
When you are creating your mock, the class definition of ZfcUser\Controller\Plugin\ZfcUserAuthentication is not being found. So PHPUnit creates a mock that only extends this class for your test. If the class was available then PHPUnit will use the actual class to extend when making its mock, which will then use the parent classes/interfaces.
You can see this logic here: https://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/PHPUnit/Framework/MockObject/Generator.php
if (!class_exists($mockClassName['fullClassName'], $callAutoload) &&
!interface_exists($mockClassName['fullClassName'], $callAutoload)) {
$prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";
if (!empty($mockClassName['namespaceName'])) {
$prologue = 'namespace ' . $mockClassName['namespaceName'] .
" {\n\n" . $prologue . "}\n\n" .
"namespace {\n\n";
$epilogue = "\n\n}";
}
$cloneTemplate = new Text_Template(
$templateDir . 'mocked_clone.tpl'
);
So if there is no class or interface, PHPUnit will actually create one itself so that the mock will meet the type hinting of original class name. However, any parent classes or interfaces will not be included because PHPUnit is not aware of them.
This would be due to not including the proper namespace in your test or having a problem in your autoloader. It is difficult to tell without actually seeing the entire test file.
Alternatively rather than mocking ZfcUser\Controller\Plugin\ZfcUserAuthentication, you could mock the Zend\Mvc\Controller\Plugin\PluginInterface in your test and pass that into the plugin manager. Though if you are type-hinting for the plugin in your code, your test still won't work.
//Mock the plugin interface for checking authorization
$authMock = $this->getMock('Zend\Mvc\Controller\Plugin\PluginInterface');
// Some expectations of the authentication service.
$authMock -> expects($this->any())
-> method('hasIdentity')
-> will($this->returnValue(true));
$authMock -> expects($this->any())
-> method('getIdentity')
-> will($this->returnValue($ZfcUserMock));
$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock);
I just made an example for the FlashMessenger plugin. You should just use the ControllerPluginManager to override the ControllerPlugin. Make sure that your application bootstrap calls setApplicationConfig();
<?php
namespace SimpleTest\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class SimpleControllerTest extends AbstractHttpControllerTestCase {
public function testControllerWillAddErrorMessageToFlashMessenger()
{
$flashMessengerMock = $this->getMockBuilder('\Zend\Mvc\Controller\Plugin\FlashMessenger', array('addErrorMessage'))->getMock();
$flashMessengerMock->expects($this->once())
->method('addErrorMessage')
->will($this->returnValue(array()));
$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->get('ControllerPluginManager')->setService('flashMessenger', $flashMessengerMock);
$this->dispatch('/error/message');
}
}?>

Resources