Create XML dataset with the same table name as initial data set in DBUnit? - unit-testing

I'm trying to create an initial DB state in DB Unit like this...
public function getDataSet() {
$primary = new \PHPUnit\DbUnit\DataSet\CompositeDataSet();
$fixturePaths = [
"test/Seeds/Upc/DB/UpcSelect.xml",
"test/Seeds/Generic/DB/ProductUpcSelect.xml"
];
foreach($fixturePaths as $fixturePath) {
$dataSet = $this->createXmlDataSet($fixturePath);
$primary->addDataSet($dataSet);
}
return $primary;
}
Then after my query I'm attempting to call this user-defined function...
protected function compareDatabase(String $seedPath, String $table) {
$expected = $this->createFlatXmlDataSet($seedPath)->getTable($table);
$result = $this->getConnection()->createQueryTable($table, "SELECT * FROM $table");
$this->assertTablesEqual($expected, $result);
}
The idea here is that I have an initial DB state, run my query, then compare the actual table state with the XML data set representing what I expect the table to look like. This process is described in PHPUnit's documentation for DBUnit but I keep having an exception thrown...
PHPUnit\DbUnit\InvalidArgumentException: There is already a table named upc with different table definition
Test example...
public function testDeleteByUpc() {
$mapper = new UpcMapper($this->getPdo());
$mapper->deleteByUpc("someUpcCode1");
$this->compareDatabase("test/Seeds/Upc/DB/UpcAfterDelete.xml", 'upc');
}
I seem to be following the docs...how is this supposed to be done?

This was actually unrelated to creating a second XML Dataset. This exception was thrown because the two fixtures I loaded in my getDataSet() method both had table definitions for upc.

Related

Doctrine ORM produce different output when using manual 'select'

I'm a beginner with Doctrine ORM (v2.5.5) and Silex (v2.0.4)/Symfony (v3.1.6). I need to output my Date field to the YYYY-MM-DD format. Let's say I have this annotation and getter method on my Entity:
// src/App/Entity/Tnkb.php (simplified)
// 'expire' field
/**
* #ORM\Column(type="date")
*/
protected $expire;
// getter
public function getExpire()
{
return !is_object($this->expire) ? new \DateTime() : $this->expire->format('Y-m-d');
}
Here's my simplified controller for debugging purpose:
$app->get('/debug', function() use ($app) {
$tnkbRepo = $app['orm.em']->getRepository('\App\Entity\Tnkb');
$normalizer = new \Symfony\Component\Serializer\Normalizer\ObjectNormalizer();
$encoder = new \Symfony\Component\Serializer\Encoder\JsonEncoder();
$normalizer->setCircularReferenceHandler(function($obj){
return $obj->getId();
});
$serializer = new \Symfony\Component\Serializer\Serializer(array($normalizer), array($encoder));
$qb = $tnkbRepo->createQueryBuilder('c')
->setMaxResults(1);
//$query = $qb->getQuery(); // [1] <<-- this line produce proper YYYY-MM-DD format
//$query = $qb->select('c.expire')->getQuery(); // [2] <<-- this (manual select) line produce DateTime object.
$results = $query->getResult();
return $serializer->serialize($results, 'json');
});
With the first [1] line uncommented I got the proper output I wanted:
[more json output here]...,"expire":"1970-10-25",...
But with the second [2] line uncommented (I intendedly omitted other fields for testing) I got the following output, which wasn't what I expected:
[{"expire":{"timezone":{"name":"UTC","location":{"country_code":"??","latitude":0,"longitude":0,"comments":""}},"offset":0,"timestamp":25660800}}]
I also noticed, with the [2] line Doctrine seems to ignore my entity's getter method (I tried returning empty string). I expect the output will be the same as the [1] case, it makes me curious. My questions are:
How do I achieve the same proper YYYY-MM-DD format with the [2] version?
And why are they produce different output format?
Thank you.
UPDATE
More simplified /debug controller for testing (no serialization):
$app->get('/debug', function() use ($app) {
$tnkbRepo = $app['orm.em']->getRepository('\App\Entity\Tnkb');
$qb = $tnkbRepo->createQueryBuilder('c');
// [1a] normal query. doesn't return Entity, getExpire() isn't called.
/*$query = $qb->select('c.expire')
->setMaxResults(1)->getQuery();*/
// [2a] partial query. returns Entity, getExpire() called.
/*$query = $qb->select('partial c.{id,expire}')
->setMaxResults(1)->getQuery();*/
$results = $query->getResult();
var_dump($results);die;
});
Updated Entity method getExpire():
// src/App/Entity/Tnkb.php (simplified)
// 'expire' field
/**
* #ORM\Column(type="date")
*/
protected $expire;
protected $dateAsString = true;
protected $dateFormat = 'Y-m-d';
// getter
public function getExpire()
{
return ($this->expire instanceof \DateTime) ? $this->dateOutput($this->expire)
: $this->dateOutput(new \DateTime());
}
protected function dateOutput(\DateTime $date) {
if ($this->dateAsString) {
return $date->format($this->dateFormat);
}
return $date;
}
Controller dump results:
[1a] normal query:
// non-entity
array(1) { [0]=> array(1) { ["expire"]=> object(DateTime)#354 (3) { ["date"]=> string(26) "1970-10-25 00:00:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } } }
[2a] partial object query:
// array of entity
array(1) { [0]=> object(App\Entity\Tnkb)#353 (23) { /* more properties */...["expire":protected]=> object(DateTime).../* more properties */
I found out this is normal behaviour with Doctrine, it has something to do with Partial Objects. See my comment below. Link: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/partial-objects.html
I don't think it's good practice to return a \DateTime sometimes but a formatted string other times. However maybe you have your reasons for doing this.
The only reason I can think of the difference in results is if Doctrine calls getters on the properties when loading an entity. I tested with a simple class which has the same expire property and getter. Returning the class still had the serialized (not formatted) \DateTime object, which would suggest that at some point your getter is being called and the property set to a new \DateTime.
My recommendation is to look at the DateTimeNormalizer provided by Symfony in 3.1. If you can't upgrade to 3.1 then you can easily build your own one. Then you can be sure you'll always have consistent \DateTime format in all your responses. You can all remove the ->format(...) from your getter then and always return a \DateTime object. I think this is a much cleaner approach.

Is there possibility to load all rows of table to array in JTable of Joomla 2.5?

I use Joomla 2.5 framework.
I have simple table such as:
int id
string itemid
Now I just want to load all 'itemid's to array using JTable. Is here possibility?
Everything about the JTable Class only deals with one row, as soon as multiple rows are involved, you have two options:
1) Use the solution you gave.
2) Override the load() function in your Table class. This can be done as under:
Put the following load() function in your Table class:
function load($key = null)
{
$db = $this->getDBO();
$query = $db->getQuery(true);
$query->select($key);
$query->from($this->getTableName());
$db->setQuery($query);
$row = $db->loadRowList();
if ($db->getErrorNum())
{
$this->setError($db->getErrorMsg());
return false;
}
// Check that we have a result.
if (empty($row))
{
return false;
}
//Return the array
return $row;
}
Now call the load() function with a single argument 'itemid', and get the array you want.
This worked for me, hope it helps you too.
Solution that I decided to use is inherit model class from JModelList:
class modelModelcomponent extends JModelList
{
protected function getListQuery()
{
// Create a new query object.
$db = JFactory::getDBO();
$query = $db->getQuery(true);
// Select some fields
$query->select('itemid');
// From the hello table
$query->from('#__table');
return $query;
}
}

Same Instances header ( arff ) for all my database queries

I am using InstanceQuery , SQL queries, to construct my Instances. But my query results does not come in the same order always as it is normal in SQL.
Beacuse of this Instances constucted from different SQL has different headers. A simple example can be seen below. I suspect my results changes because of this behavior.
Header 1
#attribute duration numeric
#attribute protocol_type {tcp,udp}
#attribute service {http,domain_u}
#attribute flag {SF}
Header 2
#attribute duration numeric
#attribute protocol_type {tcp}
#attribute service {pm_dump,pop_2,pop_3}
#attribute flag {SF,S0,SH}
My question is : How can I give correct header information to Instance construction.
Is something like below workflow is possible?
get pre-prepared header information from arff file or another place.
give instance construction this header information
call sql function and get Instances (header + data)
I am using following sql function to get instances from database.
public static Instances getInstanceDataFromDatabase(String pSql
,String pInstanceRelationName){
try {
DatabaseUtils utils = new DatabaseUtils();
InstanceQuery query = new InstanceQuery();
query.setUsername(username);
query.setPassword(password);
query.setQuery(pSql);
Instances data = query.retrieveInstances();
data.setRelationName(pInstanceRelationName);
if (data.classIndex() == -1)
{
data.setClassIndex(data.numAttributes() - 1);
}
return data;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
I tried various approaches to my problem. But it seems that weka internal API does not allow solution to this problem right now. I modified weka.core.Instances append command line code for my purposes. This code is also given in this answer
According to this, here is my solution. I created a SampleWithKnownHeader.arff file , which contains correct header values. I read this file with following code.
public static Instances getSampleInstances() {
Instances data = null;
try {
BufferedReader reader = new BufferedReader(new FileReader(
"datas\\SampleWithKnownHeader.arff"));
data = new Instances(reader);
reader.close();
// setting class attribute
data.setClassIndex(data.numAttributes() - 1);
}
catch (Exception e) {
throw new RuntimeException(e);
}
return data;
}
After that , I use following code to create instances. I had to use StringBuilder and string values of instance, then I save corresponding string to file.
public static void main(String[] args) {
Instances SampleInstance = MyUtilsForWeka.getSampleInstances();
DataSource source1 = new DataSource(SampleInstance);
Instances data2 = InstancesFromDatabase
.getInstanceDataFromDatabase(DatabaseQueries.WEKALIST_QUESTION1);
MyUtilsForWeka.saveInstancesToFile(data2, "fromDatabase.arff");
DataSource source2 = new DataSource(data2);
Instances structure1;
Instances structure2;
StringBuilder sb = new StringBuilder();
try {
structure1 = source1.getStructure();
sb.append(structure1);
structure2 = source2.getStructure();
while (source2.hasMoreElements(structure2)) {
String elementAsString = source2.nextElement(structure2)
.toString();
sb.append(elementAsString);
sb.append("\n");
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
MyUtilsForWeka.saveInstancesToFile(sb.toString(), "combined.arff");
}
My save instances to file code is as below.
public static void saveInstancesToFile(String contents,String filename) {
FileWriter fstream;
try {
fstream = new FileWriter(filename);
BufferedWriter out = new BufferedWriter(fstream);
out.write(contents);
out.close();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
This solves my problem but I wonder if more elegant solution exists.
I solved a similar problem with the Add filter that allows adding attributes to Instances. You need to add a correct Attibute with proper list of values to both datasets (in my case - to test dataset only):
Load train and test data:
/* "train" contains labels and data */
/* "test" contains data only */
CSVLoader csvLoader = new CSVLoader();
csvLoader.setFile(new File(trainFile));
Instances training = csvLoader.getDataSet();
csvLoader.reset();
csvLoader.setFile(new File(predictFile));
Instances test = csvLoader.getDataSet();
Set a new attribute with Add filter:
Add add = new Add();
/* the name of the attribute must be the same as in "train"*/
add.setAttributeName(training.attribute(0).name());
/* getValues returns a String with comma-separated values of the attribute */
add.setNominalLabels(getValues(training.attribute(0)));
/* put the new attribute to the 1st position, the same as in "train"*/
add.setAttributeIndex("1");
add.setInputFormat(test);
/* result - a compatible with "train" dataset */
test = Filter.useFilter(test, add);
As a result, the headers of both "train" and "test" are the same (compatible for Weka machine learning)

How to get the first member of the related collection in JPQL

I have Product table which has a related table Images with a relation 1:M.
Class Product {
private Integer productId;
private String productName;
....
....
....
private List<Image> productImageList;
....
....
....
}
Class Image{
private Integer imageId;
private String imageName;
}
Class ProductLite{
private Integer productId;
private String productName;
private String imageName;
}
I am trying a JPQL query where I want to query to fetch products and the first image from the productImageList and returning a ProductLite object using the new constructor.
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<ProductLite> getAllProductLite() {
Query q = em.createQuery("SELECT NEW com.mycomp.application.entity.ProductLite(p.productId, p.productName, p.productImageList.get(0).getImageName())"
+ " from Product p"
+ " ORDER by p.productName");
List<ProductLite> prods = q.getResultList();
return prods;
}
But for some reason I am not able to get it to work. I get a NoViableException. So I tried moving the logic of getting the first image (getImage() method) to the Product Entity so in the query I could just call the getImage(). Even that does not seem to work.
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [SELECT NEW com.meera.application.entity.ProductLite(distinct p.productId, p.productName, p.getImage()) from Product p, IN(p.productImageList) pil where p.category.categoryCode = :categoryCode ORDER by p.productName ], line 1, column 52: unexpected token [distinct].
Internal Exception: NoViableAltException(23#[452:1: constructorItem returns [Object node] : (n= scalarExpression | n= aggregateExpression );])
Any help is appreciated.
First, you cannot call methods in entity class from your JP QL query. Second, to use the order of entities in list, you need persisted order.
To create column for order to the join table between image and product, you have to add
#OrderColumn-annotation to the productImageList. For example:
#OrderColumn(name = "myimage_order")
//or dont't define name and let it default to productImageList_order
#OneToMany
private List<Image> productImageList;
Then you have to modify query to use that order to choose only first image:
SELECT NEW com.mycomp.application.entity.ProductLite(
p.productId, p.productName, pil.imageName)
FROM Product p JOIN p.productImageList pil
WHERE INDEX(pil) = 0
ORDER by p.productName

Testing NHibernate with SQLite "No Such Table" - schema is generated

I'm trying to use an in-memory SQLite database to test my data layer which is provided by NHibernate.
I've read a load of blogs and articles about getting this setup but I'm now very confused as to why it isn't working.
The problem - when I run a unit test I get the error 'no such table: Student'. The articles I've read suggest this is because the schema isn't getting generated, or, the connection is being closed between my SchemaExport and query. I've checked everywhere I can think of and can't see how either of these scenarios are occuring.
My test output log looks like this:
OPEN CONNECTION
drop table if exists "Student"
drop table if exists "Tutor"
create table "Student" (
ID integer,
Name TEXT,
DoB DATETIME,
TutorId INTEGER,
primary key (ID)
)
create table "Tutor" (
ID integer,
Name TEXT,
primary key (ID)
)
NHibernate: INSERT INTO "Student" (Name, DoB, TutorId) VALUES (#p0, #p1, #p2); select last_insert_rowid();#p0 = 'Text1', #p1 = 01/12/2010 14:55:05, #p2 = NULL
14:55:05,750 ERROR [TestRunnerThread] AbstractBatcher [(null)]- Could not execute query: INSERT INTO "Student" (Name, DoB, TutorId) VALUES (#p0, #p1, #p2); select last_insert_rowid()
System.Data.SQLite.SQLiteException (0x80004005): SQLite error
no such table: Student
at System.Data.SQLite.SQLite3.Prepare(String strSql, SQLiteStatement previous, String& strRemain)
at System.Data.SQLite.SQLiteCommand.BuildNextCommand()
at System.Data.SQLite.SQLiteCommand.GetStatement(Int32 index)
at System.Data.SQLite.SQLiteDataReader.NextResult()
at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader()
at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd)
14:55:05,781 ERROR [TestRunnerThread] ADOExceptionReporter [(null)]- SQLite error
no such table: Student
DISPOSE
CLOSING CONNECTION
Originally I was using my own code for the connection/session management but have moved to the code in this blog post translated to C# and with a couple changes to the DBConfig method and some debug statements to show the state of the connection.
private FluentNHibernate.Cfg.Db.IPersistenceConfigurer GetDBConfig()
{
return SQLiteConfiguration.Standard
.ConnectionString((ConnectionStringBuilder cs) => cs.Is(CONNECTION_STRING))
.ProxyFactoryFactory("NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu")
.Raw("connection.release_mode", "on_close");
}
I added the on_close after reading this
My test code looks like this:
[Test]
public void CanGetStudentById()
{
using (var scope = new SQLiteDatabaseScope<StudentMapping>())
{
using (ISession sess = scope.OpenSession())
{
// Arrange
var repo = new StudentRepository();
repo.Save(new Student() { Name = "Text1", DoB = DateTime.Now });
// Act
var student = repo.GetById(1);
// Assert
Assert.IsNotNull(student);
Assert.AreEqual("Text1", student.Name);
}
}
}
What have I overlooked here?
Update: I created a copy of the class that connects to an SQLite file DB and it worked fine. So it has to be something to do with the connection being closed.
If you change your test method to the following, does it work?
[Test]
public void CanGetStudentById()
{
using (var scope = new SQLiteDatabaseScope<StudentMapping>())
{
using (ISession sess = scope.OpenSession())
{
// Arrange
sess.Save(new Student() { Name = "Text1", DoB = DateTime.Now });
// Act
var student = sess.Get<Student>(1);
// Assert
Assert.IsNotNull(student);
Assert.AreEqual("Text1", student.Name);
}
}
}
I would hazard to guess that your StudentRepository is opening its own session and hence doesn't see the table.