I am trying to use a real Sql Server connection to run my php unit test in Laravel because SqLite does not have the function I am trying to test.
The database name is "Test" and the connection details have all been added to database.php.
I have changed my phpunit.xml env variables to refer to the new test database.
<env name="DB_CONNECTION" value="test_sqlserver"/>
<env name="DB_DATABASE" value="Test"/>
Now, when I try to run a simple test with a class that use the RefreshDatabase trait, or even the DatabaseMigrations trait, it result in the following error:
Symfony\Component\Console\Exception\InvalidOptionException: The "--drop-views" option does not exist.
It can be reproduced with the following test:
<?php
namespace Tests\Unit\Actions;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class GetResourcesExceedingHoursByPayPeriodWeeksActionTest extends TestCase
{
use RefreshDatabase;
protected $dropViews = false;
/** #test */
public function it_can_get_resources_exceeding_hours_by_pay_period_weeks()
{
}
}
Edit
The solution bellow proposed by Daniel did not solve the issue. Inspiring from that however, I added the following method:
protected function migrateFreshUsing()
{
$seeder = $this->seeder();
return array_merge([
// '--drop-views' => $this->shouldDropViews(),
// '--drop-types' => $this->shouldDropTypes(),
],
// $seeder ? ['--seeder' => $seeder] : ['--seed' => $this->shouldSeed()]
);
}
It resolved the error, however, now phpunit does not output any result. (I added a simple assertion $this->assertTrue(false);)
Full class code:
<?php
namespace Tests\Unit\Actions;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class GetResourcesExceedingHoursByPayPeriodWeeksActionTest extends TestCase
{
use RefreshDatabase;
protected $dropViews = false;
protected function migrateFreshUsing()
{
$seeder = $this->seeder();
return array_merge([
// '--drop-views' => $this->shouldDropViews(),
// '--drop-types' => $this->shouldDropTypes(),
],
//$seeder ? ['--seeder' => $seeder] : ['--seed' => $this->shouldSeed()]
);
}
/** #test */
public function it_can_get_resources_exceeding_hours_by_pay_period_weeks()
{
$this->assertTrue(false);
}
}
Edit 13:00
Adding the new tag in the phpunit.xml did not help to add output to the console.
However, I found the line causing the issue, as when it is commented, phpunit does output to the console.
To test correctly, I extended from the base TestCase class instead of the one shown above.
Then I added
$this->seed(DatabaseSeeder::class); as the first line of my test, which resulted in no output. Commenting it would allow phpunit to output, but obviously, the test would work with the dummy assertion, but not with anything that requires seeding.
With further investigation it seems that the issue is caused by 3 seeders that uses a third party package to seed from a csv file.
Every other seeder run fine.
I will keep investigating this path and update my question with the solution.
How can I test using a real Sql server database?
I'm not familiar enough with using SqlServer with Laravel, but this answer might be what you're looking for.
It's often the case that there's a default method that doesn't mesh well with a specific use case.
Writing this into your Tests\TestCase class might solve the issue.
/**
* Refresh a conventional test database.
*
* #return void
*/
protected function refreshTestDatabase()
{
if (! RefreshDatabaseState::$migrated) {
$this->artisan('migrate:fresh', [
'--drop-views' => $this->shouldDropViews(),
'--drop-types' => $this->shouldDropTypes(),
]);
$this->app[Kernel::class]->setArtisan(null);
RefreshDatabaseState::$migrated = true;
}
$this->beginDatabaseTransaction();
}
EDIT Jan 13 2023 12:23 pm
For the PhpUnit not outputting anything issue, reference this answer. Let me know in the comments if you don't get an explicit error displayed on your terminal.
Related
I am new into Kotlin and trying to learn, how to fetch Data with retrofit and store this data into a Room DB. But as soon as i start the Activity where this process takes place i get a NullPointerException.
EDIT: As far as i could find out now, my "database" in the RoomViewmodel class is still NULL when i want to access it, even though i have an override oncreate function, where it is created
Here is also a link to the GitHub repository from the mini-project I'm working on: https://github.com/Engin92/Dog_Breeds/tree/RoomDatabase
here is my complete errorlist:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.dogbreeds, PID: 14803
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.dogbreeds/com.example.Breedlist.activity.DetailedViewActivity}: kotlin.KotlinNullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3782)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3961)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2386)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:8178)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
Caused by: kotlin.KotlinNullPointerException
at com.example.Breedlist.activity.DetailedViewActivityRepository.getBreeds(DetailedViewActivityRepository.kt:23)
at com.example.Breedlist.activity.DetailedViewActivityViewModel.getAllBreedList(DetailedViewActivityViewModel.kt:23)
at com.example.Breedlist.activity.DetailedViewActivity.onCreate(DetailedViewActivity.kt:42)
at android.app.Activity.performCreate(Activity.java:8086)
at android.app.Activity.performCreate(Activity.java:8074)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1313)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3755)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3961)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2386)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:8178)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
The important part of DetailedViewActivity
class DetailedViewActivity : AppCompatActivity() {
lateinit var breedRecyclerView: RecyclerView
lateinit var detailedViewActivityViewModel: DetailedViewActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detailed_view)
breedRecyclerView = findViewById(R.id.breedRecyclerView)
detailedViewActivityViewModel = ViewModelProviders.of(this).get(
DetailedViewActivityViewModel::class.java)
if(isOnline(this))
{
detailedViewActivityViewModel.getBreedsFromAPIAndStore()
}
else
{
Toast.makeText(this,"No internet connection. Showing cached list!",Toast.LENGTH_LONG).show()
}
detailedViewActivityViewModel.getAllBreedList().observe(this, Observer<List<CurrentBreedResponseItem>> { breedList ->
Log.e(MainActivity::class.java.simpleName,breedList.toString())
setUpBreedRecyclerView(breedList!!)
})
} ....
the class RoomViewModel, where i build the DB (where i Think the error is, the var database is still NULL, after trying to access (write/read) it)
class RoomViewModel : Application() {
companion object {
var database: BreedDatabase? = null
}
override fun onCreate() {
super.onCreate()
database = Room.databaseBuilder(applicationContext, BreedDatabase::class.java, "breed_db").fallbackToDestructiveMigration().build()
}
}
getBreeds function in DetailedViewActivityRepository:
fun getBreeds() : LiveData<List<CurrentBreedResponseItem>>
{
return RoomViewModel.database!!.currentBreedDao().getAllBreeds()
}
getAllBreedList function in DetailedViewActivityViewModel
fun getAllBreedList(): LiveData<List<CurrentBreedResponseItem>>
{
return detailedViewActivityRepository.getBreeds()
}
There are two problem in your code. The first one is very clear you are trying to access a null object and you are getting NullPointerException. So be careful when you use !! operator.
The reason you are getting it is, inside your RoomViewModel your database instance is null.
class RoomViewModel : Application() {
companion object {
var database: BreedDatabase? = null
}
override fun onCreate() {
super.onCreate()
database = Room.databaseBuilder(applicationContext, BreedDatabase::class.java, "breed_db")
.fallbackToDestructiveMigration().build()
}
}
You may think you are initializing the database instance in onCreate() but the onCreate() is not getting called. The reason is to make the application class work you need to add it to your AndroidManifest.xml file.
Solution:
Add this RoomViewModel class to your AndroidManifest.xml file like this.
<application
android:name=".view.RoomViewModel"
android:allowBackup="true"
We do it as application tag's name attribute as you can see above.
This will fix your null pointer exception. But your program will again crash, because you are using Kotlin and do make room work with kotlin this thing needs to be added in your app level build.gradle file.
kapt "android.arch.persistence.room:compiler:1.1.1"
After adding it to your app level build.gradle inside dependencies block. Sync your project and run it should work.
If you want to learn more about Room Database in Android, you can check this Room Database Tutorial.
Hope this will help you.
I am using IRetryAnalyzer for running failed test cases and using IAnnotationTransformer for setting annotation at run time. For #Test using data provider its giving strange result.
I have set retry limit 3, that is test should re-run 3 times. Issue is :
If test fails for first data set, then it retries 3 times (as expected). Then for all remaining data set - re-run count is 2. I am not sure, its 2 retries or its 1 run 1 retry.
Here is class implementing data provider:
#Test(dataProvider = "data-source")
public void toolbarActionsOnShapes(String selectShape)
throws InterruptedException {
Assert.assertTrue(false);
}
#DataProvider(name = "data-source")
public Object[][] allShapes() {
return new Object[][] { { "Rectangle" }, { "Circle" }, { "Triangle" }
};
}
}
On running this i get output :
https://drive.google.com/open?id=1FxercluPinPiOOUAZKe_dMa6NvVMCE0j
For every set of data, if test fails - there should be 3 retries. Dummy project zip is attached for reference.
https://drive.google.com/open?id=1Mt7V2TO4TWRKU9dN4FIFzprkDingUKaE
Thanks !!
This is due to a bug that exists in TestNG 7.0.0-beta1. Please see GITHUB-1946 for more details.
I went ahead and fixed this as part of my pull request PR-1948
Please make use of TestNG 7.0.0-SNAPSHOT to get past this problem. This should be part of the upcoming TestNG 7.0.0-beta2 (or) 7.0.0 (final release). Its not decided on this part yet.
I am trying to integrate Hystrix javanica into my existing java EJB web application and facing 2 issues with running it.
When I try to invoke following service it always returns response from fallback method and I see that the Throwable object in fallback method has "com.netflix.hystrix.exception.HystrixTimeoutException" exception.
Each time this service is triggered, HystrixCommad and fallback methods are called multiple times around 50 times.
Can anyone suggest me with any inputs? Am I missing any configuration?
I am including following libraries in my project.
project libraries
I have setup my aspect file as follows:
<aspectj>
<weaver options="-verbose -showWeaveInfo"></weaver>
<aspects>
<aspect name="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect"/>
</aspects>
</aspectj>
Here is my config.properties file in META-INF/config.properties
hystrix.command.default.execution.timeout.enabled=false
Here is my rest service file
#Path("/hystrix")
public class HystrixService {
#GET
#Path("clusterName")
#Produces({ MediaType.APPLICATION_JSON })
public Response getClusterName(#QueryParam("id") int id) {
ClusterCmdBean clusterCmdBean = new ClusterCmdBean();
String result = clusterCmdBean.getClusterNameForId(id);
return Response.ok(result).build();
}
}
Here is my bean class
public class ClusterCmdBean {
#HystrixCommand(groupKey = "ClusterCmdBeanGroup", commandKey = "getClusterNameForId", fallbackMethod = "defaultClusterName")
public String getClusterNameForId(int id) {
if (id > 0) {
return "cluster"+id;
} else {
throw new RuntimeException("command failed");
}
}
public String defaultClusterName(int id, Throwable e) {
return "No cluster - returned from fallback:" + e.getMessage();
}
}
Thanks for the help.
If you want to ensure you are setting the property, you can do that explicitly in the circuit annotation itself:
#HystrixCommand(commandProperties = {
#HystrixProperty(name = "execution.timeout.enabled", value = "false")
})
I would only recommend this for debugging purposes though.
Something that jumps out to me is that Javanica uses AspectJ AOP, which I have never seen work with new MyBean() before. I've always have to use #Autowired with Spring or similar to allow proxying. This could well just be something that is new to me though.
If you set a breakpoint inside the getClusterNameForId can you see in the stack trace that its being called via reflection (which it should be AFAIK)?
Note you can remove commandKey as this will default to the method name. Personally I would also remove groupKey and let it default to the class name.
I have a CakePHP application hosted on AWS Elastic Beanstalk. Because of the multiple EC2 instances I will use in the future I want to store my PHP sessions in a database. AWS provides a very nice library for storing PHP sessions in their DynamoDB database. See http://goo.gl/URoi3s
Now I putted the AWS SDK in my vendors folder and created an access wrapper for it (a plugin):
<?php
Configure::load('aws');
require_once VENDORS . 'autoload.php';
use Aws\Common\Aws;
class AwsComponent extends Component
{
private $_aws;
public function __construct()
{
$this->_aws = Aws::factory(array(
'key' => Configure::read('Aws.key'),
'secret' => Configure::read('Aws.secret'),
'region' => Configure::read('Aws.region')
));
}
public function getClient($service)
{
return $this->_aws->get($service);
}
}
The wrapper is working well, I already implemented some S3 stuff. Now for the session handler i added the following code to my AppController.php:
public $components = array('Aws.Aws');
public function beforeFilter()
{
$this->_setSessionStorage();
}
private function _setSessionStorage()
{
$client = $this->Aws->getClient('dynamodb');
$client->registerSessionHandler(array(
'table_name' => 'sessions'
));
}
The AWS's internal registerSessionHandler() is executed (tested it) but the session is not beeing stored into the DynamoDB table. Of course I created the table before and if I add the call to the AWS library directly to my webroot/index.php before dispatcher is loaded everything works fine.
I think the problem is that my code is executed after CakePHP calls session_start(). So what is the best way to implement that? http://goo.gl/kUFUIR doesn't help me, I don't want to rewrite the AWS library for beeing compatible with the CakePHP interface.
So what is the best way to implement that? http://goo.gl/kUFUIR
doesn't help me, I don't want to rewrite the AWS library for beeing
compatible with the CakePHP interface.
This is in fact the best way. And this does not mean to reinvent the wheel, abstraction in OOP means that you make things available in a generic interface that can be replaced with something else. You wrap a foreign API or code in an API compatible to your system, in this case a CakePHP application.
Wrap the vendor lib in a AwsSession adapter that implements the CakeSessionHandlerInterface. This way it's API compatible with other session adapters in the case you change it and it might be even solve your core problem, because CakeSession will take care of the initialization.
Your component is initialized after the session in CakePHP, when the controller is already instantiated and then is initializing all its components. So this happens at a pretty late time. Your alternative is to stop CakePHP from initializing the session, I never had a need to do so, so no idea without looking it up myself. Dig in CakeSession. Even if you manage to do so, other components like the default Auth adapter depends on being able to work with Sessions, so you have to take care of the issue that your component has to be loaded before Auth as well. Pretty fragile system with lots of possbile points of failure. Seriously, go for the Session adapter, guess its a lot less painful to get it working this way.
By a quick look at the DynamoDB Session documentation this seems to be pretty easy. Extend the regular session handler and overload only the init and garbage collection of it to add the Aws API calls there, no guarantee this is right but seems to be easy.
What I end up with in CakePHP 3.
src/Network/Session/DynamoDbSession.php
<?php
namespace App\Network\Session;
use Aws\DynamoDb\DynamoDbClient;
use Cake\Core\Configure;
class DynamoDbSession implements \SessionHandlerInterface
{
private $handler;
/**
* DynamoDbSession constructor.
*/
public function __construct()
{
$client = new DynamoDbClient(Configure::read('DynamoDbCredentials'));
$this->handler = $client->registerSessionHandler(array(
'table_name' => Configure::read('DynamoDbCredentials.session_table')
));
}
public function close()
{
return $this->handler->close();
}
public function destroy($session_id)
{
return $this->handler->destroy($session_id);
}
public function gc($maxlifetime)
{
return $this->handler->gc($maxlifetime);
}
public function open($save_path, $session_id)
{
return $this->handler->open($save_path, $session_id);
}
public function read($session_id)
{
return $this->handler->read($session_id);
}
public function write($session_id, $session_data)
{
return $this->handler->write($session_id, $session_data);
}
}
Activate it in config/app.php file:
'Session' => [
'defaults' => 'php',
'handler' => [
'engine' => 'DynamoDbSession'
],
'timeout' => (30 * 24 * 60)
]
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