Using the .setUp() and .tearDown() function in RUnit testsuite - mysql

I have the following issue with my R tests. I have test functions that need to alter the database, compute the results, check if these results are equal to the test values and clean the database. I am trying to do using transactions from MySQL and dbBegin(con) and dbRollback(con) functions from RMySQL.
I was trying to run the following code:
.setUp <- function() {
dbBegin(con)
}
.tearDown <- function() {
dbRollback(con)
}
test.function1 <- function() {
....
}
test.function2 <- function() {
....
}
with the test suite
test.suite <- defineTestSuite("example",
dirs = file.path("tests"),
testFileRegexp = '*.r')
test.result <- runTestSuite(test.suite)
printTextProtocol(test.result)
However, when I run multiple functions I get the
Error in .local(conn, statement, ...) :
could not run statement: Duplicate entry '-1' for key 'PRIMARY'
Which means that I never rollback what I write in my database.
Could anyone indicate what is wrong in the code above and/or how to write tests in R + RMySQL to test inside transactions?
Thanks,
Vladimir

This is a proper way of testing R functions that alter the MySQL database. I was getting the error because of a typo.

Related

SQLAlchemy ORM function works in main script but fails in Pytest

Using Postgres, SQLAlchemy 1.4 ORM, Python 3.7, Pytest.
I have a script in myproject/src/db.py and the tests for it are located in myproject/tests/.
In db.py I have a function to drop any given table, it works as expected:
async def delete_table(self, table_name):
table = self.meta.tables[table_name]
async with self.engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all(sync_engine, [table], checkfirst=True))
It gets called like:
asyncio.run(db.delete_table('user'))
In conftest.py I have a fixture like this:
#pytest.fixture(scope='function')
def test_drop_table():
def get_delete_tables(table_name):
return asyncio.run(DB.delete_table(table_name))
return get_delete_tables
In test.py I run the test like this:
#pytest.mark.parametrize('function_input, expected',
[('user', 'user'),
pytest.param('intentional failure', 'intentional failure',
marks=pytest.mark.xfail(reason='testing incorrect table name fails'))])
def test_drop_table(test_drop_table, function_input, expected):
# Drop the table
test_drop_table(function_input)
# Check that it no longer exists
with pytest.raises(KeyError) as error_info:
test_table_commits(function_input, expected)
raise KeyError
assert error_info.type is KeyError
When I run this test I get this error:
self = <postgresdb.PostgresDb object at 0x7f4bbd87cc18>, table_name = 'user'
async def delete_table(self, table_name):
> table = self.meta.tables[table_name]
E KeyError: 'user'
I verified that this table can be dropped in the main script. I then recommit the table, verify it is present, and then try to drop it with the test but will continually receive a KeyError that the table is not present even though when checking the database that table is actually present.
I'm not sure what to test or adjust in the code to get Pytest working with this function. I appreciate any help!
I think for the first time it deletes the table named user, but the second input in pytest.mark.parametrize is also the name user, so it may be throwing error. If you need to test 2 different scenarios, it's better to have 2 different test functions. By doing this, you can have all your code under with pytest.raises(KeyError) as error_info in the 2nd test function.

MySQL binding multiple parameters to a single query

I have a MySql database, and I'm connecting to it from a .Net app using Dapper. I have the following code:
await connection.ExecuteAsync(
"DELETE FROM my_data_table WHERE somedata IN (#data)",
new { data = datalist.Select(a => a.dataitem1).ToArray() },
trans);
When I do this with more than a single value, I get the following error:
MySqlConnector.MySqlException: 'Operand should contain 1 column(s)'
Is what I'm trying to do possible in MySql / Dapper, or do I have to issue a query per line I wish to delete?
Your original code was almost fine. You just need to remove the parentheses around the parameter. Dapper will insert those for you:
await connection.ExecuteAsync(
"DELETE FROM my_data_table WHERE somedata IN #data",
new { data = datalist.Select(a => a.dataitem1).ToArray() },
trans);

Catch mysql error in R

I'am working with r and mysql database and I don't know how catch an insert data into table error in mysql.
I have this:
status <- tryCatch({ AnalyzerDb.insert_data_frame(dataset) })
But when I run the code I have an error:
Error en mysqlExecStatement(conn, statement, ...) :
RS-DBI driver: (could not run statement: Duplicate entry '00001002-2014-01-17 00:00:00' for key 'PRIMARY')
But when I view the value in status var, the variable has NULL value.
Thanks
tryCatch works by you defining functions that handle errors and warnings. If you don't supply tryCatch with any such handles, nothing will be done.
The documentation has several examples illustrating this, as well as a long description outlining how it works.
Something like this:
tryCatch(log("a"),error = function(e) cat("I found an error"))
I have defined this function:
myDivideTryCatchInformation <- function(StatusVector, status ) {
if (class(status) == "try-warning") {
StatusVector[4] = StatusVector[4] + 1
} else if (class(status) =="try-error") {
StatusVector[5] = StatusVector[5] + 1
} else {
StatusVector[3] = StatusVector[3] + 1
}
return(StatusVector)
}
params: StatusVector: this is a vector with 6 positions: XXX, rows, ok, warnings, errors, XXX
and status is the result of try function.

How to delete a SQL database table in R

I created a table using dbWriteTable in R (and then appended more data to it), using two commands:
dbWriteTable(my_db, name="dallas_flights", value=nov_trim, overwrite = TRUE, row.names = FALSE)
dbWriteTable(my_db, name="dallas_flights", value=dec_trim, append = TRUE, row.names = FALSE)
After I ran my script, I deleted everything using rm(list=ls()). When I type, ls() into R, I get character(0).
However, when I try to rerun the script, and reach these two commands, I get an error indicating that the table still exists:
Error in sqliteExecStatement(con, statement, bind.data) :
RS-DBI driver: (error in statement: table dallas_flights already exists)
I do not know how to delete this table. Any advice?

Debugging ErlyDB and MySQL

I am experimenting with ErlyDB in a non-erlyweb environment and I am not having much luck.
I have a 'Thing' table for testing, and a corresponding Thing module:
-module(thing).
-export([table/0, fields/0]).
table() ->
thing.
fields() ->
[name, value].
The module itself works - I can query the database fine using ([Thing] = thing:find({name, '=', "test"})).
When I try and save a new record, however things aren't so good.
I consistently see the following error:
mysql_conn:426: fetch <<"BEGIN">> (id <0.97.0>)
mysql_conn:426: fetch <<"INSERT INTO thing(value,name) VALUES ('vtha','blah')">> (id <0.97.0>)
mysql_conn:426: fetch <<"ROLLBACK">> (id <0.97.0>)
** exception exit: {{'EXIT',{badarg,[{erlang,hd,[[]]},
{erlydb_base,'-do_save/1-fun-0-',4},
{mysql_conn,do_transaction,2},
{mysql_conn,loop,1}]}},
{rollback_result,{updated,{mysql_result,[],[],0,[]}}}}
in function erlydb_base:do_save/1
in call from erlydb_base:hook/4
in call from test_thing:test/0
called as test_thing:test()
The table exists, the credentials work, and the SQL itself is fine, as I can execute the command directly on the database.
The code I am using to save is:
erlydb:start(mysql, Database),
Thing = thing:new(<<"hello">>, <<"world">>),
thing:save(Thing),
Is there something I am missing?
Is there some way of viewing some more helpful error messages from the database?
Looking at the source of erlydb_base, the exception happens when erlydb calls your thing module's db_pk_fields() function. That function should return a list, but apparently it doesn't.
I can confirm that altering the code in erlydb.erl fixes this problem (from this reference on the mailing list).
Change Line 561 of erlydb.erl from
lists:map(fun({_Name, _Atts} = F) -> F;
(Name) -> {Name, []}
end, lists:usort(DefinedFields)),
To:
lists:map(fun({_Name, _Atts} = F) -> F;
(Name) -> {Name, []}
end, DefinedFields),