| Agile ALM
By Michael Hüttermann
XStream is an XML serializer and deserializer. In this article, based on chapter 8 of Agile ALM, author Michael Hüttermann discusses how XStream can be used when you need an object from an XML structure or vice versa.
You may also
be interested in…
|
XStream is an XML serializer and deserializer. It can be used
for various situations where you need an object from an XML structure or vice
versa. It is easy to use and the XML is very clean.
If you are defining test data of flat objects in Java, you need
to write some glue code. If you need to define object trees, you will soon
realize that Java is the wrong language and that it is very time consuming to
initialize every child object, set the values, and assign them to the parent
object. This could be fine if you need it at a few points of you application,
but if there are object hierarchies that are predefined, you should extract
them. An XML structure is one way we could describe object hierarchies. In
fact, it is a very common approach because there are a lot of tools available
and you don’t need to worry about using the wrong character sets.
Let’s try with a simple example. First, add the XStream dependency to your pom.xml:
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.3.1</version>
</dependency>
Now, let’s write a simple Java bean:
public class User {
private String firstName;
private String lastName;
public User(String firstName, String lastName) {
this.lastName = lastName;
this.firstName = firstName;
}
...
}
To serialize User with XStream, we can use XStream’s API as
follows:
XStream xstream = new XStream();
User user = new User("Paul", "Breitner");
String xml = xstream.toXML(user);
Depending on the package name you use in your Java class, the result
is similar to this:
<org.agile.alm.entities.User>
<firstName>Paul</firstName>
<lastName>Breitner</lastName>
</org.agile.alm.entities.User>
As you can see, there are some package definitions in the XML
file. If you want a cleaner XML structure, you can define so-called aliases in
XStream. You can define an alias either as an annotation or as a special
definition. If you can control your objects, then annotations are a very handy
way to write and forget about the aliases, especially if you use that object
multiple times on different code positions. Serializing with a defined alias
looks like this:
XStream xstream = new XStream();
xstream.alias("user",User.class);
User user = new User("Paul", "Breitner");
String xml = xstream.toXML(user);
Working with XStream annotations looks as follows:
import com.thoughtworks.xstream.annotations.XStreamAlias;
@XStreamAlias("user")
public class User {
private String firstName;
private String lastName;
...
}
In the Java code, serializing based on annotations looks like
this:
XStream xstream = new XStream();
xstream.processAnnotations(User.class);
User user = new User("Paul", "Breitner");
String xml = xstream.toXML(user);
For deserializing objects from XML, you must go the following
way:
<user>
<firstName>Paul</firstName>
<lastName>Breitner</lastName>
</user>
It becomes even more helpful if you have larger object trees for
deserialization. Normally, the objects of your model are already defined. With
XStream, you can reuse complex objects to drive your dataProvider in TestNG.
Now let’s go back to TestNG. To use your list of objects as a dataProvider, you
can use a helper function that transforms it into an array of arrays, as seen
in listing 1.
Listing 1 TestNG test class reading data from XML
via XStream
import org.agile.alm.entities.User;
import com.thoughtworks.xstream.XStream;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestMe {
…
protected <T> List<T> readListData(String filename) { #A
return (List<T>) xstream.fromXML(this.getClass().getClassLoader().
getResourceAsStream(filename));
}
public static <T> Object[][] getObjectArray(List<T> elements) { #1
Object[][] values = new Object[elements.size()][1];
int counter = 0;
for (T t : elements) {
values[counter][0] = t;
counter++;
}
return values;
}
@DataProvider(name = "users")
public Object[][] createData() {
List<User> users = readListData("users.xml");;
return getObjectArray(users); #B
}
…
}
#A Reads the XML with XStream
#1 Transforms list into an array of array
#B Returns data for the tests
Transforming the list into an array of arrays (#1) is done to
fit into TestNG’s dataProvider
format.
Summary
The reuse of your model can be very handy in data-driven
functional tests. There are ten or more parameters you want to check in your
user interface. A parameter list becomes even more complicated and results in a
lot of work. If you build your user interface around your model, then this is
the way you should go. XStream makes it very easy to fill in the important
parts of your model.
Here are
some other Manning titles you might be interested in: