Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to match text against templates in unit tests

I have a class that consume an XML file and produce text output based on the input. Both the input and output are rather complex and the output can also include things not in the input: e.g. include timestamps and the results from live data that are not controlled by the input - with other words: the class is not a pure input-output transformation.

I would like to test the resulting text output using JUnit. As the produced text can vary in many different ways based on the input, I would like to be able to match specific parts of the output against some sort of template in each test. Each template should allow for some simple text substitutions and also for ranges in the text that should not be matched.

The question is whether any such frameworks already exists?

One very low-level possibility would be to use some fancy regular expressions to match the text, but I think these will be a bit too limited for our use as you don't have enough context in regular expressions...

EDIT: Two comments:

  • One of the functions of the class is the ability to do certain simple types of aggregation of data and calculations (e.g. sums) based on the input. This I would like to test, without testing the rest of the generated text output as well.
  • I wish it was possible to make changes to the existing code base, but it is a very large chuck of legacy code that I really don't want to refactor. So introduction of mock services or testing of smaller pieces will not be possible.
like image 750
Tonny Madsen Avatar asked Jan 10 '13 11:01

Tonny Madsen


2 Answers

Perhaps this is indicative that you need to be testing at a lower level ? i.e. testing the contributing components rather than the entire output en masse. I would hope that you can arrange your code/tests such that you can provide an immutable set of inputs (perhaps using mocking where necessary) and consequently the outputs won't change.

A few high-level tests would be useful (to confirm results integration) and you could perhaps do that by a simple string comparison (just to confirm stuff is being integrated properly) but I think the effort should perhaps be put in at a more granular level.

Otherwise I suspect you may want a diff-like tool, and this library looks like it may be useful.

like image 81
Brian Agnew Avatar answered Sep 22 '22 05:09

Brian Agnew


I would use Groovy for writing the unit tests, because this is one of Groovy's strength, see

  • Unit Testing
  • Using JUnit 4 with Groovy
  • Unit Testing - Groovy
  • Spock

But Groovy is also superb for handling XML, see

  • Prozessing XML
  • Reading XML using Groovy's XmlSlurper

A little example for summing up some XML attributes:

// multiline string, very complex XML content :-)
def input = '''\
<list>
    <summand value='13' time='10:40' text='Compare me!'/>
    <summand value='1' />
    <summand value='4' />
    <summand value='2' />
    <summand value='7' />
</list>'''

// reading XML via XmlSlurper
def list = new XmlSlurper().parseText(input)

// Prints 13
println list.summand[0].@value

// collect all summand values, prints 27
println list.summand.collect { [email protected]() }.sum()

You can find a good tutorial about testing in the MEAP Making Java Groovy or look at this presentation.

Groovy has also template support. But with the XML support it is very easy to compare only certain attributes and not the whole tag content to skip some attributes like the timestamp you mentioned. Therefore you don't need to compare templates. For an example add this source to the skript above:

// compare the first summand tag, skipping the time attribute
assert [list.summand[0][email protected](), list.summand[0].@text] == [13, 'Compare me!']

To learn Groovy, I recommend the Groovy Koans. See also Adding Groovy Tests to a Maven Java Project.

Update:
I would not compare the XML against each other, instead I would unit test single values as desribed in my answer. But if you go the complete way I would use the following approach:

  • get the XML from your business logic
  • remove temporary (not comparable values with Groovy)
  • generate a template from GStringTemplateEngine (see link above) without the not comparable values
  • compare both XMLs via http://xmlunit.sourceforge.net/
    You can find an example on Updating XML with XmlSlurper the end of the page.
like image 20
ChrLipp Avatar answered Sep 25 '22 05:09

ChrLipp