I’m wanting to write unit tests to check that for given database responses, certain objects are create/things are done.
The problem I have, is that when mocking the database response, I have to manually go through and define the exact behavior that database/database accessor will respond with, for each test.
What would be much easier, would be if I ran certain queries against production database, and captured the returned objects and saved them as a test resource, and then used those as my objects to returned by my mocked database.
If the objects in question implemented Serializable
then this would be pretty straight forward, as I could just save them as text.
But what if they don’t implement Serializable
? How else can I capture java objects for testing?
You already captured the crux of your question: a non-Serializable
object can be difficult to mock in the way you are describing.
What you are looking to do is less of a unit test and more of a system test. You can implement this as a unit test (e.g. JUnit) but it will not be efficient.
It sounds like you are essentially trying to unit test using real DAOs of some type.
- Create a process to initialize a simple database to a known state. This could be anything from restoring a real (but small) backup of a “real” DBMS such as Oracle or SQL Server, or a snapshot of a Sqlite database.
- Initialize your test by connecting to the database.
- Run your test by getting data from the DB as a Java object (i.e. DAO) and doing whatever you need to do.
- Scrap the DB: next test reinitializes so tests do not interfere with each other.
There may also be mock facilities in your DB abstraction layer (e.g. Hibernate) but I have not looked into this recently. If you are using JDBC directly then good luck: it is a low-level protocol that does not deal with concerns such as unit testing databases.
There are multiple ways of doing this.
The one most closely resembling your production setup would be to use an in-memory database like H2 and plug that into your system while testing. Then you can just export the rows in question and start your test with importing a bunch of rows. H2 can be run in “MySQL mode” if you’re running MySQL; that makes it mainly compatible with MySQL, at least for the basic statements. It’s not 100% compatible though as e.g. escaping works differently. But once you have figured that out you don’t have to do that again. If you are only dealing with somewhat standard data-types and simple queries, I’d say this is the best option, as all you have to mock is the datasource. I prefer going this route for that reason. This works fine with direct JDBC or some abstraction layers like Spring JDBC as well as Hibernate.
Another common practice is to serialize your objects into JSON, using the Jackson parser for example, especially, since a lot of applications are already dealing with JSON and the serializers/deserializers are readily available. Jackson can serialize objects into JSON even if they are not Serializable, as long as they follow the Java Beans Specification. It derives the property names by inspecting the class. The downside is, that you have to mock your DAO, not only switch out the datasource. You can simply copy & paste the serialized objects into your test or read from a file and then deserialize. There are a lot of modules available for non-standard types and you can write your own serializers as well.
0
I have saved live, instantiated objects with state to a binary file and then returned them to life, just by adding water like sea monkeys, the only caveat is that all objects must implement serializable:
public static void saveObjects(Animal a, Car b, Thing c, String filePath) throws FileNotFoundException, IOException{
FileOutputStream fos = new FileOutputStream(filePath);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(a);
oos.writeObject(a);
oos.writeObject(c);
}
public static Object[] loadObjects(String filePath) throws FileNotFoundException, IOException, ClassNotFoundException{
FileInputStream fis = new FileInputStream(filePath);
ObjectInputStream ois = new ObjectInputStream(fis);
Object o[] = new Object[3];
o[0] = ois.readObject();
o[1] = ois.readObject();
o[2] = ois.readObject();
return o;
}
For Java, one solution could be a capture-replay tool like testrecorder. The current version will allow you to annotate methods of interest with @Recorded
:
- making it capture all data arriving to or departing from this method (arguments, result, exceptions, this-pointer)
- translating this data into a runnable unit tests
There are some other tools like that (reCrash, Jthor) for special purposes.