When I test DAO module in JUnit, an obvious problem is: how to recover testing data in database?
For instance, a record should be deleted in both test methods testA() and testB(), that means precondition of both test methods need an existing record to be deleted. Then my strategy is inserting the record in setUp() method to recover data.
What’s your better solution? Or your practical idea in such case? Thanks
I'd make a method called createRecord(). It may be a test-method as well. And whenever you need to create a record, call that method from your other test methods.
Maybe DBUnit can help you out.
It allows to have a TEST database in a predefined state before executing each test. Once it set upped, it's really easy to test database driven applications.
A simple solution is to roll back the transaction after the test (for example in tearDown()). That way, the tests can make all the changes they like but they won't change the database (don't forget to turn autoCommit off for the connection).
There is a drawback, though: If a test fails, you can't peek at the database to figure out why. Therefore, most of my tests clean the database before they run and they use autoCommit so I can see the last state where it failed, run a fixed SQL query against the data, etc.
Bozho is correct, of course, but just to add a bit of detail:
If possible, unit tests set up their data before manipulating it, and then clean up after themselves. So ideally you would not be trampling on existing data (perhaps copied from production) for testing, but setting some up as part of the test; that's practically the only way you can be assured that your test will be testing what you intended.
Related
I have DAO using Spring's jdbcTemplate with Create Read Update (no Delete) operation.
Create method have ID parameter which is unique key in table.
Except mocking DAO, how can I actually test create without getting constraint violation?
Using random ID still can fail sometimes
Should I override setAutoCommit to avoid adding record? is it still consider a valid unit test?
Must I delete in SQL the record in database beforehand or is there spring option for this types of tests?
Or should I consider it as integration test and not unit test?
EDIT
I'm using Oracle, I can't use sequence for creating values for the ID
We have a few Data sources exists (not for testing) in production
It really depends on what is the purpose of such a test, not all the tests are "unit tests" with this respect.
If, for example, the goal is to test the "service" that encapsulates a business logic, but from this service, sometimes there are calls to DAO, then probably the best way is to just mock the DAO as you suggest.
In this case, DAO obviously won't be covered by this test, but the service will.
If the purpose is to test SQL statements (and I assume that DAO contains nothing but SQL statements + maybe transforming them into the domain object), then mocking is not an option.
In this case, the test should include calls to some kind of database, but in this case, it's not called a unit test anymore (a unit test is something that runs really fast and only in memory, no DBs, no I/O, etc.) I'll call this an integration test (as you also suggest), but different people have probably different names for this kind of tests.
In practice, we need both kinds of tests because they test different things
So, how to test this?
First of all the decision should be made, which database should be used, there are 3 approaches here:
Run with a real database, shared between the users, tests assume its pre-installed
Run with an in-memory database
Run the DB docker image of the DB when the test suite runs, and destroy it afterwards
While the discussion of which approach is better is very interesting on its own, its out of scope for this question IMO, each choise has its implications.
Once you're done with this decision, you should decide how to work with this database from the code.
Usually spring tests use the following pattern:
open a transaction before the test
run the test (change the data, even change the schema - add columns, tables if you want). Do assertions
Regardless the tests result, rollback the transaction so that the data will be just like before the test
So if you follow this approach for all your tests, they'll start with "empty" data state so that no constraint violations are expected
This effectively solves also the "deletion of the record" question because the data will be deleted anyway when the transactions is rolled back.
Now regarding the Deletion of the record outside a transaction.
An obvious approach is to execute an sql of deletion right from the test (outside the DAO), so that DAO (a production code won't be changed)
You can inject DataSource/JDBCTemplate right into the test (Spring test perfectly supports this) and call the required SQL from there
I have a bug that manifest himself only when database is slow. It applies to even simplest database operations (select, etc).
I would like to create test, where I force my database to be slow. How to do that?
I use Spring Boot, Spring Data, Hibernate, MariaDB.
Ideally, I want the 'slow database' part to be completely contained in the test code, in my java application. That way, test will be completely automated and self-contained.
I want to slow down database access only for one test (not globally, for all access).
I was proposed to introduce database trigger (BEFORE SELECT) with sleep
But this is not flexible, because it slows down every access, not access just for one test.
I see four possible solutions for this problem.
You don't have to create slow database, you can create slow connection to the database. If you run database on a different (Virtual) machine, there are systems that help simulating shitty internet connections by delaying network responses randomly.
You can use sleep(10) function that is provided by your database, this would require "injecting" it into SQL query or override method for the purpose of test and replace SELECT with SELECT SLEEP(10).
Simulate stress-test on the database with mysqlslap if you use mysql.
Another solution, a bit stupid tho, you can use spring-aop and attach a delay aspect before and after the DAO method execution with random small sleep. This way you have control over it, don't have to modify existing code and let spring make the job of doing the delay without integration into real-system. Not that stupid after all. This one is quite flexible and I think I would go with it. Easiest to setup.
If it's stupid, but it works, it's not stupid.
I had a similar need when developing on a SQL Server DB.
To simulate a slow query you can use (but this is specific to SQL Server):
select * from TABLE
WAITFOR DELAY '00:00:45'--to simulate 45 seconds of delay
If you want to write a Spring Boot Test, maybe you can use the #SpyBean annotation
#SpyBean
SomeBeanCallingTheDatabase someBeanCallingTheDatabase;
//...
// in the test method
doAnswer(answer-> {
Thread.sleep(300L); //any value here
return answer.callRealMethod();
})
.when(someBeanCallingTheDatabase)
.find(any());
// call the service using the bean above
The easy answer is to write a test repository class that has a Thread.sleep embedded in it.
credit: this answer was provided by https://stackoverflow.com/users/37213/duffymo in the comment.
Here's the scanario:
I am working on a DAO object which uses hibernate criteria API to form a number of complicated queries to perform certain tasks on the database (keyword search across multiple fields for example).
We need to unit test this to ensure that the generated query is correct for various scenarios. One way of testing it -which could be preferable- would be to test the hibernate criteria is created correctly by checking it at the end and mocking the database interaction. However this is not desirable as firstly it's kinda cheating (it's merely duplicating what the code would be doing) and also it doesn't check if the criteria itself causes hibernate to barf or when it goes to database it causes issues.
The option to use is then run the query against a test database. However, for historical reasons there is no static test database (one that code be checked in as part of the code for example) and the remit of my project does not allow me to embark on creating one, we have to content with testing against a shared development database that's periodically refreshed with production data.
When theses refreshes happen, the data behind the tests could change too, and this would make our unit tests brittle. We can get over it by not using exact numbers in tests but it's not really adequate testing that way.
The question is then: what do people do in cases like this to make tests less brittle? One option that I have in mind is to run a native SQL that does the same query (behaviourally - it doesn't have to be exact same as the query generated by hibernate) to get the expected number and then run the DAO version to see if it matches. This way, the behaviour of the query can be always implemented in the initial native SQL and you will always have the correct numbers.
Any feedback on this or other ideas on how to manage this situation would be greatly appreciated.
A.
UPDATE:
With regards to hsqldb/h2/derby suggestions, I am familiar with them but the company is not ready to go down that route just yet and doing it piecemeal on just one test case won't be suitable.
With regards to my earlier suggestion I would like to elaborate a bit more - consider this scenario:
I want to ensure that my relatively complicated keyword search returns 2100 matches for "John Smith".
In order to find the expected number, I would have analyzed my database and found out the number using a SQL Query. What is the downside of having that query as part of the test, so that you will always know the you are testing the behaviour of the criteria?
So basically the question is: if for some reason you could not have a static data set for testing, how would you perform you integration tests in a non-brittle way?
One approach could be to use in-memory database like Apache Derby or HSQLDB, and prepopulate it with data before test start using DBUnit.
UPDATE: Here is a nice article about the aproach.
I agree with Andrey and Bedwyr that the best approach in the long term is to create an hsqldb database specifically for testing. If you don't have the option of doing that, then your solution seems like an appropriate one. You can't test everything, but you don't want to test nothing either. I've used this approach a few times for testing web services against integration databases etc. But remember that this database has to be maintained as well, if you add new columns etc.
You have to decide what you're trying to test. You don't want to test hibernate, you don't want to test that the database is giving what you've asked for (in terms of SQL). In your tests, you can assume that hibernate works, as does the database.
You say:
We need to unit test this to ensure that the generated query is
correct for various scenarios. One way of testing it -which could be
preferable- would be to test the hibernate criteria is created
correctly by checking it at the end and mocking the database
interaction. However this is not desirable as firstly it's kinda
cheating (it's merely duplicating what the code would be doing) and
also it doesn't check if the criteria itself causes hibernate to barf
or when it goes to database it causes issues.
Why should hibernate barf on the criteria you give it? Because you're giving it the wrong criteria. This is not a problem with hibernate, but with the code that is creating the criteria. You can test that without a database.
It has problems when it gets to the database? Hibernate, in general, creates the sql that is appropriate to the criteria and database dialect you give it, so again, any problem is with the criteria.
The database does not match what hibernate is expecting? Now you are testing that the criteria and the database are aligned. For this you need a database. But you're not testing the criteria any more, you're testing that everything is aligned, a different sort of test.
So actually, it seems to me you're doing an integration test, that the whole chain from the criteria to the structure of the database works. This is a perfectly valid test.
So, what I do is in my tests to create another connection to the database (jdbc) to get information. I execute SQL to get number of rows etc, or check that an insert has happened.
I think your approach is a perfectly valid one.
However, for historical reasons there is no static test database (one that code be checked in as part of the code for example) and the remit of my project does not allow me to embark on creating on
All you need to do is fire up H2 or similar - put some entities in it and execute your integration tests. Once you've done this for a few tests you should be able to extract a data setup utility that creates a schema with some test data that you can use for all the integration tests if you feel the need.
If I have a method which establishes a database connection, how could this method be tested? Returning a bool in the event of a successful connection is one way, but is that the best way?
From a testability method, is it best to have the connection method as one method and the method to get data back a seperate method?
Also, how would I test methods which get back data from a database? I may do an assert against expected data but the actual data can change and still be the right resultset.
EDIT: For the last point, to check data, if it's supposed to be a list of cars, then I can check they are real car models. Or if they are a bunch of web servers, I can have a list of existant web servers on the system, return that from the code under test, and get the test result. If the results are different, the data is the issue but the query not?
THnaks
First, if you have involved a database, you are no longer unit testing. You have entered integration (for connection configuration) or functional testing land. And those are very different beasts.
The connection method should definitely be separate from data fetch. In fact, your connection should come from a factory so that you can pool it. As far as testing the connection, really all you can test is that your configuration is correct by making a connection to the DB. You shouldn't be trying to test your connection pool, as that should probably be a library someone else wrote (dbcp or c3p0). Furthermore, you probably can't test this, as your unit/integration/function tests should NEVER connect to a production level database.
As for testing that your data access code works. That's functional testing and involves a lot of framework and support. You need a separate testing DB, the ability to create the schema on the fly during testing, insert any static data into table, and return the database to a known clean state after each tests. Furthermore, this DB should be instantiated and run in such a way that 2 people can run the tests at once. Especially if you have more than 1 developer, plus an automated testing box.
Asserts should be against data that is either static data (list of states for example, that doesn't change often) or against data that is inserted during the test and removed afterwords so it doesn't interfere with other tests.
EDIT: As noted, there are frameworks to assist with this. DBUnit is fairly common.
You can grab ideas from here. I would go for mock objects when unit testing DB.
Otherwise, if application is huge and you are running long and complex unit tests, you can also virtualize your DB server and easily revert it to a saved snapshot to run again your tests on a known environment.
Using my Acolyte framework ( https://github.com/cchantep/acolyte ) you can mimick any JDBC supported DB, describing cases (how to handle each query/update executed) and which resultset/updatecount to returned in each case (describe fixtures as row list for queries, count for update).
Such connection can be directly used passing instance where JDBC is required, or registered with unique id in JDBC URL namespace jdbc:acolyte: to be available for code getting connection thanks to JDBC URL resolution.
Whatever way of creating connection, Acolyte keep each one isolated which is right for unit test (without having extra cleanup to do on a test DB).
As persistence cases can dispatched to different isolated connection, you no longer need a big-all-in-on-hard-to-manage db (or fixtures file): it can be easily split in various connection, e.g. one per persistence method/module.
My Acolyte framework is usable either in pure Java, or Scala.
If the goal is to test method functionality, not the database SP or SQL statement, then you may want to consider dependency injection in sense of data provider interface. In other words, your class uses an interface with methods returning data. The default implementation uses the database. The unit test implementation has several options:
mocking (NMock, Moq, etc.), great way, I live mocking.
in-memory database
static database with static data
I don't like anything but first. As a general rule, programming to interfaces is always much more flexible.
For database connection establish testing: you could let the connection execute a very simple SQL as testing method. Some application servers have such configuration, following snippet is from JBoss DB configuration:
<!-- sql to call on an existing pooled connection when it is obtained from pool
<check-valid-connection-sql>some arbitrary sql</check-valid-connection-sql>
I try to write an big test class.
I'm using Junit, Hibernate and TopLink and H2 database. Before this I used EJB3Unit (including Hibernate and H2).
My test class has 57 test methods. If I run all test at once randomized one or more test fails. If I run each test alone, I get no error.
Has anyone an idea what's going wrong? And how I can prevent this?
For each test method I create a new in memory database with a different name.
I create a new entitymanagarfactory and entitymanagar instance.
I've disabled second level caching.
I create all table via script (no error occurs so database is really fresh).
I do some db actions and test.
I clear session and em.
I drop all object in my in-memory database
I shut down the database
I close em and emf.
Have I to do more?
Thanks a lot...
It seems that there is dependency among the tests.
ideally you should restore the database to its original state after each test by using a tearDown method (in JUnit 4, use the #After annotation).
If you're already doing that then the dependency is more subtle. To find out its cause I suggest you start doing a binary search on the tests: comment out half of your tests. If the random failure persists then comment out half of the remaining half (and so on). If the failure disappears then the problem is in the other half: uncomment and comment out the other half. This process will converge quite quickly.
Good hunting.
Dependencies are a possibility for this random failing.
An other reason can be the order of elements in a collection. Once i was writing a test and depended on the first element. It was not sorted so i was not sure that the object i was asking was always the same.