how to run non-static after-tests with JUnit and SpringRunner - spring

We have some methods in production code annotated with #Transaction(propagation = REQUIRES_NEW). Thus, annotating the test class with #Transactional/#Rollback still leads to polluted database. I would like to have method that could delete records from the relevant tables when ALL of the tests of this class have completed. However, #AfterClass method is bound to be static, so how could I access my Spring-provided EntityManager and/or Spring Data repositories. The solution we use at the moment is to annotate test class with #DirtiesContext(mode=AFTER_CLASS) however this leads to really painful experiences if you forget to also override datasource URL for this test class.
So, can this be achieved in more idiomatic Spring/JUnit manner?

I have an regression suite which runs against QA environments. Since it exercises the application, by its very nature, it modifies the database. For these tests, I have specific test users and each test creates any records it is dependent on. During this setup and during execution of the tests, any created database records are tracked in local collection. At the end of the test, i.e. during the cleanUp method, the records are removed from the database. I also use a name and test run identifier that makes it easy to distinguish these records from real data in case of any cleanup issues. Not sure this would meet your idea of idiomatic Spring/JUnit, but this works for us and keeps our QA environment data reasonably clean.

Related

Is there ever a reason to use unit tests in a specific order?

This question came about because I had a set of unit tests fail and I looked up this question. Another stack overflow user was asking about creating sequential unit tests in a certain order, but users were responding that you should never do that and all unit tests should be independent. What about testing where you have some state data. In my case I am testing a class that performs CRUD data operations on a database. I want to test that the class first inserts a record, updates a record, deletes a record etc. It seems reasonable to test writing the record, updating the same record, and then deleting a record as a way of confirming that the crud operations are working. Any thoughts?
I think I found the correct answer to this which is to use database mocking. See existing stack overflow article:
How to unit test an object with database queries

How to test #transactional methods

I have a spring application and it needs transactions.
How would i write a test to see if the #Transactional annotation is working correctly?
I have a service class with an autowired repo, and methods like
#Transactional(propagation = Propagation.REQUIRED)
public boolean saveObjectToDb(Object a){
repo.save(a)
}
Here's what you can do. It isn't necessarily the best idea but here goes:
#Test
public void testTransactionality() {
Method[] methods = YourClassWhateverItsCalled.class.getMethods();
Optional<Method> findFirst = Arrays.stream(methods)
.filter(method -> method.getName()
.equals("saveObjectToDb"))
.findFirst();
Transactional[] annotationsByType =
findFirst.get().getAnnotationsByType(Transactional.class);
assertThat(annotationsByType).isNotEmpty();
}
So the specific assertion i'm using here could be more on-point for sure. However...
The reason that this isn't the best idea is because you're just testing that the annotation is there. You don't test that things are actually saved to the database. Testing things manually in a development environment has its merits and you should definitely keep in mind that you don't need to test 100% of your code. 80% is a good rule of thumb.
If you wanted to test that data is actually saved to the database that's harder. Approaches include:
Actually having a test database that you run these kinds of tests against. Debugging these tests can be awful but they are at least fast sometimes because the database is already up and running before the test comes along.
Spinning up an in memory database for each test of this kind and performing transactional inserts, updates, deletes against that. This can also be a pain because it requires a lot in terms of the test harness and the tests are slow. However if they're written well (you don't want your data from one test in a JUnit test class to end up in another without developers realising) then it can be super maintainable. Relatively speaking.
I would love to learn of more approaches here but generally speaking this is a difficult area to get right.
Update: I should note that if you test database access be sure to keep your tests unit tests. Nothing worse than trying to figure out what's happening with a database access test which spans 20 different classes. PURE. HELL.

Spring and JUnit, the difference of annotating classes and methods with #Transaction?

I would like to understand that if I annotate my junit class with #Transactional, Spring will create only one transaction which will be shared among my #Test methods and rolled back at the end. Whereas if instead I mark each single #Test with #Transactional, a new transaction will be created and rolled back on a #Test basis. I didn't quite find the expected behaviour in the official documentation (link).
Putting #Transactional on a class level is equivalent to putting it on each of the test methods.
I don't think there is an easy way of achieving your first scenario, ie, a single transaction for all the tests. I am not sure it would make much sense anyway since tests will be executed in a random order so you cannot rely on seeing each others modifications. Of course you can always explicitly call your methods from a single uber-test with a single transaction.
#Transactional at JUnit test case class level will start new transaction before each test method and roll it back afterwards.
You cannot easily start new transaction at the beginning of test class and keep it open for the whole class, at least Spring is not supporting this. Your first workaround would be to use #BeforeClass/#AfterClass pair, but they must be static thus you don't have access to transactional manager.
But first ask yourself a question, why do you want to do this? Sounds like one test depends on the output or database side effects of the other. This is a big anti-pattern in testing, not to mention JUnit does not guarantee the order in which test methods are executed. Maybe you just need a database setup before each test case? #Before and #After are executed within the context of Spring transaction, so you can use them there.

junit test case transaction not getting committed - no errors

We have implemented JUnit4 in our application which uses Spring core & JPA with DB2.
We need to test a full functionality which retrieves data from one database and merges into another database.
Test case for retrieving the data from 1st database is written and it is running without any error perfectly but the records are not store into the 2nd database.
Implementation
The TestCase class we have included the following annotations to make the test case run under a transaction if necessary,
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class})
#ContextConfiguration(locations={""})
#TransactionConfiguration(transactionManager="defaultTransactionManager", defaultRollback=false)
#Transactional
In the application we have a manager class to perform this operation with doSynch() method. From that method crudHelper class's txStore() method will be called to initialize and call doStore() method (in same class) to merge the entity to database.
Following are the transactional declarative through out this test case logic
TestCase testSynch() - #Transactional(propagation=Propagation.SUPPORTS)
Manager doSynch() - #Transactional(propagation=Propagation.NEVER)
CRUDHelper txStore() - #Transactional(propagation=Propagation.REQUIRED)
doStore() - No Transactional annotation
doSynch() is marked as NEVER as at that point it doesn't need any transaction and in further levels such as in CRUDHelper the transaction can be marked as REQUIRED to ensure a transaction to be available.
Problem
Here when we run the test case which calls the Manager's doSynch() method to test the functionality, complete flow is working perfect except that records are not merged and no errors are thrown.
The Manager method when called from a JSP works great.
Also we tested by calling txStore() directly from test case and it also works fine.
Please let us know whether the transaction management is not proper or a work around to this issue will be of greater help. Also pls update me if the issue or environment is not clear. Thanks in advance.!!
Do you mark your methods with the #Rollback annotation?
From the JavaDoc:
Test annotation to indicate whether or
not the transaction for the annotated
test method should be rolled back
after the test method has completed.
If true, the transaction will be
rolled back; otherwise, the
transaction will be committed.

How do I do TDD correctly? When do I write tests for layers deeper than my business logic layer (i.e. DAL)?

I'm still uncertain about how best to use Mocks when doing development from the outside-in (i.e. write test first that mimics the customer's requirement)
Assume my client's requirement is "Customer can cancel her order".
I can write a test for this - Test_Customer_Can_Cancel_Her_Order.
Using TDD I then write the business class and mock the data access layer (or any layer that is 'deeper' than my business logic layer). Fantastic, my test passes and I am only testing the business layer and not any layers underneath.
Now what I am wondering is... when do I delve deeper and start writing and testing the data access layer? Directly after the top-level test? Only when I write the integration test? Maybe it doesn't matter? As long as it gets done at some point?
Just wondering...
This is a tricky question. Some people think that it's ok to test "private" code, some people think it's not. It is certainly easier, as you don't have to do complex setups to reproduce an application state that lets you test a small function that you want to add (you can test that function directly). I used to be among those people, but now I'd say that it's better to just test public APIs (create tests and setups that do and see only what the user can do and see). The reason for this is that if you only test public API, you empower yourself with unlimited re-factoring potential. If you test private code, you will have to change your tests if you choose to re-factor (making you less prone to "refactor mercilessly", thus leading to bad code).
However, I have to say, testing only public APIs do lead to more complex test frameworks.
So, to answer to your question more specifically, your next step is to try to think of a situation where there would be a problem with your current order cancellation code and write a new high-level setup to reproduce that problem. You mentioned data access tests. Create a high level setup where the database is corrupt, and test that your application behaves gracefully.
Oh, if for GUI applications, it's a little trickier, as you don't really want to get to the point where you test mouse clicks and read pixels. That's just silly. If you use (and you should) the MVC system, stop at the controller level.
Use integration tests for query and other data access testing.
I always write tests to test the DAO layer, which isn't testing business logic, but I feel it is important to test the CRUD features. This has gotten me into a lot of trouble because if my database is corrupt for some reason my tests have the high possibility of failing. What I do to prevent these DAO type tests from failing is, first do the testing in a non-production database. Then for each CRUD/DAO test I
find objects that may have been left around from a previous test and if exist I delete them.
I create objects I want to test
I update the objects I want to test
I clean up or delete the objects I created.
This sequence helps me to make sure my database is in a condition where my tests will not fail if run twice and the first time the test stopped half way in between.
Another way is to wrap your CRUD tests in a transaction and at the end of the test rollback the transaction so the database is in the state that it began.

Resources