Yii2 + Codeception: fixture without db component - database

I have a problem running unit tests in my Yii2 app with Codeception.
I have 2 databases, so in my test-local.php config file I added 2 components (class yii\db\Connection), respectively with id “db1” and “db2” , but no one with id “db”.
In my AR model, I overrided getDb() function to return "db1" component:
// MyModel.php
public static function getDb()
{
return Yii::$app->db1;
}
When I run codeception test (./vendor/bin/codecept run ...), this error occours:
[yii\di\NotInstantiableException] Failed to instantiate component or
class “db”.
I overrided the “db” property of my Fixture, :
class MyFixture extends ActiveFixture
{
public $modelClass = '\path\to\MyModel';
public $db = 'db1';
}
If I create an empty database, then add it as “db” component to config file, all works well (although tests run correctly on "db1").
How can I run codeception test with fixture without "db" component instantiate try?

Related

Camel Properties file load from Junit Integration Test

I have a Camel Route which uses CDI to load a properties file from the JBoss configuration directory ...works perfect.
What I need to do is load one of the properties that are loaded in
an Arquillian integration test I am writing.
Example:
Content of Fiddler.properties file in the JBoss configuration directory
silly.value = Laughing
serious.value = politics
Example Producer class to load properties
/**
* Create the Camel properties component using CDI #Produces
*/
#Produces
#Named("properties")
PropertiesComponent propertiesComponent() {
final PropertiesComponent component = new PropertiesComponent();
// load JBoss properties file
component.setLocation(
"file:${jboss.server.config.dir}/fiddler.properties"
);
return component;
}
A given property from the Fiddler.properties file is now available in the main Camel route as {{silly.value}} or {{serious.value}}
Problem:
What I would like to do is load/reference one of these property values from my Arquillian Integration Test … probably in the #BeforeClass method …something like below:
#RunWith(Arquillian.class)
public class MainRouteIT {
.
.
Boolean allOK = false;
#BeforeClass
public static void setupTest() throws Exception {
allOK = new testCheck(
{{silly.value}}, {{serious.value}}
);
.
.
Any idea if something like this is possible in Camel within an Arquillian test ?
Here is the solution we are using (but without Arquillian):
First define a CDI alternative for the Camel "properties" component, which will use testing property values.
Then annotate your unit test in order to use the alternate producers of Camel components.
#Alternative
public class CamelAlternatives {
#Produces
#ApplicationScoped
#Named("properties")
public PropertiesComponent propertiesComponent() {
PropertiesComponent component = new PropertiesComponent();
component.setLocations( Arrays.asList("classpath:common.properties", "classpath:testing.properties") );
return component;
}
#RunWith(CamelCdiRunner.class)
#Beans(alternatives = {CamelAlternatives.class})
public class MyUnitTest {
...
}

mockStatic: mock java.lang with PowerMock

I am trying to mock MBeanServer with Mockito, but my attempts fails.
#Test
public void testGetAllCacheProperties() {
mockStatic(ManagementFactory.class);
MBeanServer server = MBeanServerFactory.newMBeanServer();
ObjectInstance inst = server.registerMBean(new MyBeanService(), ObjectName.getInstance(SERVICE_NAME));
given(ManagementFactory.getPlatformMBeanServer()).willReturn(server);
}
I suppose to inject my mock into method that normally runs on jBoss AS 7:
#GET
public Response getAllProperties() {
MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
But it fails with exception:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
JmxMBeanServer cannot be returned by getPlatformMBeanServer()
getPlatformMBeanServer() should return MBeanServer
Update
When I try
PowerMockito.doReturn(server).when(ManagementFactory.class, "getPlatformMBeanServer");
I get exception:
java.lang.LinkageError: loader constraint violation: when resolving method "java.lang.management.ManagementFactory.getPlatformMBeanServer()Ljavax/management/MBeanServer;" the class loader (instance of org/powermock/core/classloader/MockClassLoader) of the current class, my_package_for_test_class.TestClass, and the class loader (instance of <bootloader>) for the method's defining class, java/lang/management/ManagementFactory, have different Class objects for the type javax/management/MBeanServer used in the signature
There is not possible to mock static from java.lang package, since PowerMock tries to change bite code and bite code of java.lang classes
obviously protected from modifications.
There is work around suggested by Johan Haleby.
You have to create wrapper class:
public class JmxUtils {
public static MBeanServer getPlatformMbeanServer() {
return ManagementFactory.getPlatformMBeanServer();
}
}
Then test will look like this
#RunWith(PowerMockRunner.class)
#PrepareForTest(JmxUtils.class)
public class CacheControllerTest {
//.. preconditions
given(JmxUtils.getPlatformMbeanServer()).willReturn(server);

How to load a component in console/shell

In CakePHP, how do I load a component in a shell?
To use a component in a controller, you include an array of components in a property called $components. That doesn't work for my Shell. Neither does the "on-the-fly" loading suggested in the documentation.
class MakePdfsShell extends Shell {
public $components = array('Document'); // <- this doesn't work
public function main()
{
$this->Document = $this->Components->load('Document'); // <- this doesnt work either
$this->Document->generate(); // <- this is what I want to do
}
...
I have some xml utilities that I use across some of my controllers. One of the controller launches a heavy task via the cake console, so that it can quietly run in the background via PHP CLI, while the user's request is immediately completed (once the task done, it will e-mail the results to the user).
The xml utilities are generic enough to be used in controller and shell, are too specific to the application to warrant them vendor status. The offered solution with the Lib folder does not work as in CakePhp v3 there seems to be no Lib folder.
After some quite some time spent, I managed to load my controller component to the shell task. Here is how to:
namespace App\Shell;
use Cake\Console\Shell;
use Cake\Core\App;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
use App\Controller\Component\XmlUtilitiesComponent; // <- resides in your app's src/Controller/Component folder
class XmlCheckShell extends Shell
{
public function initialize() {
$this->Utilities = new XmlUtilitiesComponent(new ComponentRegistry());
}
...
$this->Utilities can now be used across my entire shell class.
You simply don't.
If you think you have to load a component in shell your application architecture is bad designed and should be refactored.
Technically it is possible but it doesn't make sense and can have pretty nasty side effects. Components are not made to run outside of the scope of a request. A component is thought to run within the scope of a HTTP request and a controller - which is obviously not present in a shell.
Putting things in the right place
Why does XML manipulation stuff have to go into a component? This is simply the wrong place. This should go into a class, maybe App\Utility\XmlUtils for example and have no dependencies at all to the request nor controller.
The logic is properly decoupled then and can be used in other places that need it. Also if you get incoming XML the right place to do this manipulation (by using your utility class) would be inside the model layer, not the controller.
You want to learn about Separation of Concerns and tight coupling
Because you've gone just against both principles.
https://en.wikipedia.org/wiki/Separation_of_concerns
What is the difference between loose coupling and tight coupling in the object oriented paradigm?
Search before asking
You could have tried to search via Google or on SO you would have found one of these:
using components in Cakephp 2+ Shell
CakePHP using Email component from Shell cronjob
Using a plugin component from shell class in cakephp 2.0.2
...
Be aware that some of them might encourage bad practice. I haven't checked them all.
I assume that you have a component named YourComponent:
<?php
App::uses('Component', 'Controller');
class YourComponent extends Component {
public function testMe() {
return 'success';
}
}
- with cake 2., you can load your component like this
App::uses('ComponentCollection', 'Controller');
App::uses('YourComponent', 'Controller/Component');
class YourShell extends AppShell {
public function startup() {
$collection = new ComponentCollection();
$this->yourComponent = $collection->load('Your');
}
public function main() {
$this->yourComponent->testMe();
}
}
- with cake 3. you can load your component like this
<?php
namespace App\Shell;
use App\Controller\Component\YourComponent;
use Cake\Console\Shell;
use Cake\Controller\ComponentRegistry;
class YourShell extends Shell {
public function initialize() {
parent::initialize();
$this->yourComponent = new YourComponent(new ComponentRegistry(), []);
}
public function main() {
$pages = $this->yourComponent->testMe();
}
}
If you are trying to access a custom XyzComponent from a shell, then you probably have commonly-useful functionality there. The right place for commonly-useful functionality (that is also accessible from shells) is in /Lib/.
You can just move your old XyzComponent class from /Controller/Component/XyzComponent.php to /Lib/Xyz/Xyz.php. (You should rename your class to remove the "Component" suffix, e.g., "XyzComponent" becomes "Xyz".)
To access the new location, in your controller, remove 'Xyz' from your class::$components array. At the top of your controller file, add
App::uses('Xyz', 'Xyz'); // that's ('ClassName', 'folder_under_/Lib/')
Now you just need to instantiate the class. In your method you can do $this->Xyz = new Xyz(); Now you're using the same code, but it can also be accessed from your Shell.
//TestShell.php
class TestShell extends AppShell{
public function test(){
//to load a component in dis function
App::import('Component', 'CsvImporter');
$CsvImporter = new CsvImporterComponent();
$data = $CsvImporter->get();
}
}
//CsvImporterComponent.php
App::uses('Component', 'Controller');
class CsvImporterComponent extends Component {
function get(){
//your code
}
}

CakePHP Logging to Live DB During Unit Testing

I'm using DB logging in Cake 2.1, which works great.
The problem I'm having is when running Unit Tests, all logs are still getting sent to the live db rather than the test database.
All other db interactions go to test, except logging.
I do have a log fixture created and imported into the test case.
Here's my Database logger (/Lib/Log/Engine/DatabaseLogger.php)
App::uses('CakeLogInterface', 'Log');
class DatabaseLogger implements CakeLogInterface
{
public function __construct($options = array() )
{
App::import('Model', 'Log');
$this->Log = new Log;
}
public function write($type, $message)
{
$this->Log->create();
$log['type'] = ucfirst($type);
$log['date'] = date('Y-m-d H:i:s');
$log['message'] = $message;
return $this->Log->save($log);
}
}
I'm sure I'm missing some basic setting here but I can't figure this out for the life of me.
Well, in my case the problem was caused because of a bad initialization of a constructor.
You can check the update solution here:
How to choose the test DB cakePHP testing
And here:
How to override model's constructor correctly in CakePHP

Apex error: "Save error: Method does not exist or incorrect signature"

I'm currently learning apex (using the Force.com IDE), and I'm running into some trouble when writing a test for a custom controller.
The controller class is as follows:
public with sharing class CustomController {
private List<TestObject__c> objects;
public CustomController() {
objects = [SELECT id, name FROM TestObject__c];
}
public List<TestObject__c> getObjects() {
return objects;
}
}
and the test class is:
#isTest
private class ControllerTest {
static testMethod void customControllerTest() {
CustomController controller = new CustomController();
System.assertNotEquals(controller, null);
List<TestObject__c> objects;
objects = controller.getObjects();
System.assertNotEquals(objects, null);
}
}
On the objects = controller.getObjects(); line I'm getting an error which says:
Save error: Method does not exist or incorrect signature: [CustomController].getObjects()
Anyone have an idea as to why I'm getting this error?
A nice shorthand:
public List<TestObject__c> objects {get; private set;}
It creates the getter/setter for you and looks cleaner imo. As for your current issue, yes - it's hard saving code directly into production - especially with test classes in separate files.
Best to do this in a sandbox/dev org then deploy to production (deploy to server - Force.com IDE). But if you must save directly into production then I'd combine test methods with your class. But in the long run, having #test atop a dedicated test class is the way to go. It won't consume your valuable resources this way.

Resources