2

I want to unit test an API that parses a file for specific patterns.
The fragments of text returned, may be multiline and contain tabs etc. I.e. the text in the file is formatted with new lines and tabs to be readable by a user (same as we nicely indent an xml file).
Problem: Since Java does not offer the option to define such strings (which will be the expected outcome in an Assert check of the API output) how are such problems handled?
I thought e.g. to save all the expected output in a file with some kind of special character to mark beginning and end of each expected fragment but I thought that perhaps there is a standard approach to such a problem.
Is there a better option?

Update: Example:

This is an example String.    
     This is an inner part of the string.        Another part.           Another also.    
                      This is also an inner part.   
     Now an outer.   This is the outer example       string.
Cratylus
  • 52,998
  • 69
  • 209
  • 339
  • I'm confused. Is the problem picking a format? Why not pick the format your testing against? Most likely CSV or XML are formats you can look at and those are pretty canonical. – Adam Gent Nov 08 '12 at 21:56
  • When you say the returned text may be "multiline", does that mean it has '\n' characters in it? – Code-Apprentice Nov 08 '12 at 21:57
  • 1
    At least for reasonably sized strings, what's wrong with `"Hello\nWorld"`? – dimo414 Nov 08 '12 at 21:59
  • @dimo414:I don't define the string.I copy them from sample files that I will process.But I can not copy-paste them in eclipse. – Cratylus Nov 08 '12 at 22:03
  • You can't copy-paste in eclipse? Something's pretty messed up with your eclipse install then. – dimo414 Nov 08 '12 at 22:15

3 Answers3

3

UPDATE: Based on @Code-Guru's answer (which kudos for him for saying the obvious): You can easily make it so that Eclipse will insert and auto escape your literals correctly if you want to go the inline route: Windows->Preferences->Java->Editor->Typing->Escape text when pasting literals.

The sorry state of affairs is that Java does not have multiline Strings like Scala or Groovy.

The best solution I have found is to put a text file in your class path and do a

getClass().getResourceAsStream("filename");
//Then convert to string.

That will load a file named filename from the classpath relative to the getClass() class (ie in the same package).

You can use my gist here: https://gist.github.com/4041855 which uses Guava to make it easier to load files as Strings from the classpath and does some sexy caching.

If your using Maven or Gradle you will want to put those files in src/test/resources and not src/test/java.

You could obviously do some parsing of the string (ie XML) if you don't want to make many files. The other slightly overkill option is to make a readonly SQLite database or even a CSV. I have seen people do that if they have 1000s of different parameters they want to test. If thats your problem you should google JUnit Parameterized tests.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203
  • I don't really get this.In the text file `filename` that you say will be all the expected `String`s?So your suggestion is also to use a file?And how is this better from just using a `Reader`? – Cratylus Nov 08 '12 at 21:45
  • Well it really depends on what your testing. String's are nice because they are immutable and can be put in HashMaps and you can do `assertEquals` on `String`. I think I'm confused what your asking. Check my edits to see if it clarifies. – Adam Gent Nov 08 '12 at 21:47
  • See the update.This is a sample string that is what I expect from the API if it works correctly.I have many such strings.You are also saying to store them in a file?But how does CSV fit here? – Cratylus Nov 08 '12 at 21:54
  • Ignore the CSV comment. So you want to parameterize a test with many different string outputs. If thats the case I think XML is probably your best bet otherwise I'm saying to make a file for each test output. The problem with XML or any format is that you will have to use handle/know about the escaping. – Adam Gent Nov 08 '12 at 21:58
  • I did not know about the Eclipse configuration which was exactly what I needed!Thanks! – Cratylus Nov 08 '12 at 22:10
2

I don't see how the fact that Java does not allow "multiline" Strings in code is a problem. You should be able to compare against a String which has '\n' and '\t' characters in them. Whether these "expected" Strings are hardcoded in your tests or come from an input file is a design decision for your test suite.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • I already have the strings.I can not copy-paste them in the code that is the problem. BTW nice avatar... – Cratylus Nov 08 '12 at 22:01
  • @Cratylus see my updated answer on how to make that easy. To tell you the truth I find Code-Guru's icon slightly off key (maybe even offensive) for this forum... but whatever. – Adam Gent Nov 08 '12 at 22:04
  • @Cratylus What do you mean you cannot copy and paste the strings into your code? Do you mean it isn't allowed for some reason or that something unexpected happens when you try? – Code-Apprentice Nov 08 '12 at 22:07
  • Adam Gent mentioned about an Eclipse configuration on pasting I was unaware.Your answer seems to inspire him. +1 from me. – Cratylus Nov 08 '12 at 22:11
0

I would back up @Adam Gent suggestion, adding some sample code to read a resource into a String:

final InputStream is = StringFromFile.class.getResourceAsStream("data.txt");
final byte[] b = new byte[is.available()];
is.read(b);
System.out.println(new String(b, "UTF-8"));

You can embed formatting placeholders (such as %s, %d etc) and then use String.format to replace them with actual values.

If you want to have a single file with all your strings, you can maintain an xml file:

<strings>
    <string name="error 1"><![CDATA[Multiline
error
message 1]]></string>
    <string name="error 2"><![CDATA[Multiline
error
message 2]]></string>
    <string name="error 3"><![CDATA[Multiline
error
message 3]]></string>
</strings>

and import it into a map - so you can recall it with map.get(name);

You can find an example of something similar here

Community
  • 1
  • 1
thedayofcondor
  • 3,860
  • 1
  • 19
  • 28
  • He can also look at my gist which uses Guava to essential do the above.. but thanks for the backup :) – Adam Gent Nov 08 '12 at 21:59
  • Of course - I just tend to avoid using external jars for simple tasks, unless of course you use multiple non-trivial functionalities – thedayofcondor Nov 08 '12 at 22:02
  • So you are suggesting a separate file for each String?Why is this better approach than using a single file? – Cratylus Nov 08 '12 at 22:04