Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I improve my junit tests

Right my junit tests look like a long story:

  • I create 4 users
  • I delete 1 user
  • I try to login with the deleted user and make sure it fails
  • I login with one of the 3 remaining user and verify I can login
  • I send a message from one user to the other and verify that it appears in the outbox of the sender and in the inbox of the receiver.
  • I delete the message
  • ...
  • ...

Advantages: The tests are quite effective (are very good at detecting bugs) and are very stable, becuase they only use the API, if I refactor the code then the tests are refactored too. As I don't use "dirty tricks" such as saving and reloading the db in a given state, my tests are oblivious to schema changes and implementation changes.

Disadvantages: The tests are getting difficult to maintain, any change in a test affects other tests. The tests run 8-9 min which is great for continuous integration but is a bit frustrating for developers. Tests cannot be run isolated, the best you can do is to stop after the test you are interested in has run - but you absolutely must run all the tests that come before.

How would you go about improving my tests?

like image 511
flybywire Avatar asked Feb 26 '09 08:02

flybywire


People also ask

What makes a unit test good?

Unit testing guidelines demand that a good unit test should be independent, repeatable, readable, exhaustive, realistic, maintainable, fast and easy. These are the best practices you want to keep in mind in order to write effective unit tests and make sure the time invested is truly useful.


1 Answers

First, understand the tests you have are integration tests (probably access external systems and hit a wide range of classes). Unit tests should be a lot more specific, which is a challenge on an already built system. The main issue achieving that is usually the way the code is structured:

i.e. class tightly coupled to external systems (or to other classes that are). To be able to do so you need to build the classes in such a way that you can actually avoid hitting external systems during the unit tests.

Update 1: Read the following, and consider that the resulting design will allow you to actually test the encryption logic without hitting files/databases - http://www.lostechies.com/blogs/gabrielschenker/archive/2009/01/30/the-dependency-inversion-principle.aspx (not in java, but ilustrates the issue very well) ... also note that you can do a really focused integration tests for the readers/writers, instead of having to test it all together.

I suggest:

  • Gradually include real unit tests on your system. You can do this when doing changes and developing new features, refactoring appropriately.
  • When doing the previous, include focused integration tests where appropriate. Make sure you are able to run the unit tests separated from the integration tests.
  • Consider your tests are close to testing the system as a whole, thus are different from automated acceptance tests only in that they operate on the border of the API. Given this think about factors related to the importance of the API for the product (like if it will be used externally), and whether you have good coverage with automated acceptance tests. This can help you understand what is the value of having these on your system, and also why they naturally take so long. Take a decision on whether you will be testing the system as a whole on the interface level, or both the interface+api level.

Update 2: Based on other answers, I want to clear something regarding doing TDD. Lets say you have to check whether some given logic sends an email, logs the info on a file, saves data on the database, and calls a web service (not all at once I know, but you start adding tests for each of those). On each test you don't want to hit the external systems, what you really want to test is if the logic will make the calls to those systems that you are expecting it to do. So when you write a test that checks that an email is sent when you create an user, what you test is if the logic calls the dependency that does that. Notice that you can write these tests and the related logic, without actually having to implement the code that sends the email (and then having to access the external system to know what was sent ...). This will help you focus on the task at hand and help you get a decoupled system. It will also make it simple to test what is being sent to those systems.

like image 101
eglasius Avatar answered Oct 20 '22 10:10

eglasius