I want to get the output of this method myLogger.log(e) in a String variable to be able to test with Junit.
myLogger.log(e);
String s= ????
assertEqual(expected,s);
You can write a log4j appender that writes to a StringWriter. Look at this post:
How to read log4j output to a web page?
You can add an additional Appender to your Logger. Use a WriterAppender to write to a StringWriter then you change the content of your TextArea to the value of StringWriter#toString()
public static void main(String[] args) {
Logger logger = Logger.getLogger("logger");
Layout layout = new PatternLayout();
StringWriter stringWriter = new StringWriter();
WriterAppender writerAppender = new WriterAppender(layout, stringWriter);
logger.addAppender(writerAppender);
logger.error("test");
String string = stringWriter.toString();
System.out.println(string);
}
as described here.
Related
Apologies in advance - I know this has been asked a thousand times but I've looked through so many articles/documentation and I'm just so f****** lost.
I have a class that takes in an XML file and then uses DocumentBuilder to parse it into a new file that will be used as a source for other classes to use.
I need to test my method (which is void). My project is completed but I need to test.
If anyone could be so kind to show me how this would be done, I can go ahead and follow that same logic with my other classes, as 90% of the methods in my project do not return anything.
Thanks...
public class XmlToCsv {
public static void xmlToCsv(String sourceXlsFile, String sourceCsvFile, String sourceXmlFile) throws Exception {
//define the files
File stylesheet = new File(sourceXlsFile);
File xmlSource = new File(sourceXmlFile);
//create the DocumentBuilder to parse the XML file
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(xmlSource);
//input the stylesheet to transform the XML to
StreamSource stylesource = new StreamSource(stylesheet);
Transformer transformer = TransformerFactory.newInstance().newTransformer(stylesource);
//write a new output file using the stylesheet format
Source source = new DOMSource(document);
Result outputTarget = new StreamResult(new File(sourceCsvFile));
transformer.transform(source, outputTarget);
}
}
What you trying to do is actually not the way to do it. You should test ONLY the XmlToCsv class and not the classes that are used by this class (DocumentBuilderFactory, DocumentBuilder, Document, StreamSource, Transformer, Source, Result).
There are now 2 ways you can go: The clean code way, or the dirty test way.
The best solution is that you have a dependency framework for the classes you use:
public class XmlToCsv {
#Inject
DocumentBuilderFactory factory;
#Inject
StreamSource stylesource;
#Inject
TransformerFactory transformerFactory;
public void xmlToCsv(String sourceXlsFile, String sourceCsvFile, String sourceXmlFile) throws Exception {
//define the files
File stylesheet = new File(sourceXlsFile);
File xmlSource = new File(sourceXmlFile);
//create the DocumentBuilder to parse the XML file
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(xmlSource);
//input the stylesheet to transform the XML to
StreamSource stylesource = new StreamSource(stylesheet);
Transformer transformer = transformerFactory.newInstance().newTransformer(stylesource);
//write a new output file using the stylesheet format
Source source = new DOMSource(document);
Result outputTarget = new StreamResult(new File(sourceCsvFile));
transformer.transform(source, outputTarget);
}
}
Testing now can be done by injecting mocks into the injectable fields:
#RunWith(MockitoJUnitRunner.class)
public class XmlToCsvTest {
#Mock
DocumentBuilderFactory factory;
#Mock
StreamSource style source;
#Mock
TransformerFactory transformerFactory;
#InjectMocks
XmlToCsv sut; // System Under Test
#Test
public void testOk() throws Exception {
// Mocks
DocumentBuilder documentBuilder = Mockito.mock(DocumentBuilder.class);
Document document = Mockito.mock(Document.class);
// Now you control all objects created in the class and you can test if the right methods are called
// when-clauses
Mockito.when(factory.newDocumentBuilder).thenReturn(documentBuilder);
Mockito.when(documentBuilder.parse(any(File.class)).thenReturn(document);
// Add all when's here
// now call the class
sut.xmlToCsv("", "", "");
// now verify all calls
verify(factory, times(1)).newDocumentBuilder();
verify(documentBuilder, times(1)).parse(any(File.class));
// etc.
}
}
The dirty way is using PowerMockito. With PowerMockito you can override the new methods of existing classes. It is really a last resort and I wouldn't recommend it, but you can use it when you can't change the source code. It will look something like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest({XmlToCsv.class, DocumentBuilderFactory.class})
public class XmlToCsvTest {
XmlToCsv sut;
#Test
public void testXmlToCsv() throws Exception {
DocumentBuilder documentBuilder = Mockito.mock(DocumentBuilder.class);
Document document = Mockito.mock(Document.class);
//when phase
PowerMockito.mockStatic(DocumentBuilderFactory.newInstance).thenReturn(documentBuilder);
Mockito.when(factory.newDocumentBuilder).thenReturn(documentBuilder);
Mockito.when(documentBuilder.parse(any(File.class)).thenReturn(document);
// now call the class
sut.xmlToCsv("", "", "");
//now verify
verify(documentBuilder, times(1)).parse(any(File.class));
}
}
As you see the examples aren't complete, but you get the difference.
It looks like the way you'd want to test this method is to validate the expected contents of the file written to the sourceCsvFile argument, which you could do by reading in the contents after your method is called. I don't think you need to do anything with Mockito - all of your arguments are String objects, and so there's no need to create any mocks.
To test a code generator, this is the best approach I found:
Prepare a set of test cases with the same XSL: For each one, an XML input file and an expected CSV output file. Put the input files into a directory, and the expected ones into another one, but set the same names for each pair of files (case1.xml and case1.csv).
Code a JUnit class with a private method which should do the test and the comparison, and then add one #Test method for each case you want to test:
import java.io.File;
import org.apache.commons.io.FileUtils;
public class XmlToCsvTest
{
private final File inputDir=new File("my_input_xml_files");
private final File expectedDir=new File("my_expected_csv_files");
private final File generatedDir=new File("my_generated_files"); // This is just a working dir
private void xmlToCsv(String xslFile, String inputFileName)
{
try
{
File inputXmlFile=new File(this.inputDir, inputFileName + ".xml");
File outputCsvFile=new File(this.generatedDir, inputFileName + ".csv");
File expectedCsvFile=new File(this.expectedDir, inputFileName + ".csv");
xmlToCsv(xslFile, outputCsvFile.getAbsolutePath(), inputXmlFile.getAbsolutePath());
FileUtils.contentEquals(expectedCsvFile, outputCsvFile);
}
catch (Exception e)
{
fail(e.toString());
}
}
#Test
public void xmlToCsvWithCase1()
{
xmlToCsv("myFirst.xsl", "case1");
}
#Test
public void xmlToCsvWithEmptyFile()
{
xmlToCsv("myFirst.xsl", "empty");
}
#Test
public void xmlToCsvWithOneRow()
{
xmlToCsv("myFirst.xsl", "one-row");
}
...
}
Once you have mastered this technique, you can add more complexity to your tests, by adding other XSLs, with its own testing cases.
Don't forget to add the set of files to your project as resources, to become part of the source control system.
Note: This approach assumes that each output file depends only on the contents of the input file. If the generator adds some independent content (as the current date, current user, or so) a previous pre-processing must be done.
As an upshot (and I can expound by code examples if necessary), I have just realized that my REST API, written in Java, provided by CXF and served by Tomcat 7 is case sensitive when it comes to posting XML content.
Is there any way to make the XML, which usually is a marshalled representation of the entity a service creates, can be case insensitive?
I can certainly post examples of the entity class, service, and their annotations if necessary but as bare minimum, if an instance variable in the entity is private String firstName, the XML tag must be <firstName>...</firstName> and not <firstname>...</firstname> but I would like to make the latter marshall-able.
A complete solution involves a lot of work but it is perfectly possible. Following the link posted by #matiaselgart, the general solution would be
1 - Add a CXF Interceptor to manipulate the Message
2 - Read the incoming content, extract the XML, and process it with a StreamReaderDelegate to convert to lowercase
3 - Replace the content in Message with the output
The JAXB tags should be in lowercase, so the streamer could convert them easily, and be processed by JAXB unmarshaller. In your example private String firstName, the XML tag must be <firstname>...</firstname> and not <firstName>...</firstName>.
CXF Interceptor
public class CaseInsensitiveInterceptor extends AbstractPhaseInterceptor<Message> {
public CaseInsensitiveInterceptor () {
super(Phase.RECEIVE);
}
public void handleMessage(Message message) {
//Get the message body as input stream, process the xml, and set a new non-consumed inputStream into Message
InputStream in = message.getContent(InputStream.class);
InputStream bin = xmlToLowerCase (in);
message.setContent(InputStream.class, bin);
}
public void handleFault(Message messageParam) {
//Invoked when interceptor fails
}
}
Configuration
Add the interceptor in the bus or in the provider
<bean id="caseInsensitiveInterceptor" class="CaseInsensitiveInterceptor " />
<cxf:bus>
<cxf:inInterceptors>
<ref bean="caseInsensitiveInterceptor"/>
</cxf:inInterceptors>
</cxf:bus>
Case Insensitive StreamReaderDelegate
I think you can use the StreamReaderDelegate from here and convert the XMLStreamReader to InputStream using this link . The method xmlToLowerCase is called from interceptor
WARNING: I have not tested this part of the code.
private static class MyStreamReaderDelegate extends StreamReaderDelegate {
public MyStreamReaderDelegate(XMLStreamReader xsr) {
super(xsr);
}
#Override
public String getAttributeLocalName(int index) {
return super.getAttributeLocalName(index).toLowerCase();
}
#Override
public String getLocalName() {
return super.getLocalName().toLowerCase();
}
}
public InputStream xmlToLowerCase (InputStream in){
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xsr = xif.createXMLStreamReader(in);
xsr = new MyStreamReaderDelegate(xsr);
String xml = getOuterXml(xsr);
return new ByteArrayInputStream (xml.getBytes());
}
//https://coderanch.com/t/478588/XMLStreamReader-InputStream
private String getOuterXml(XMLStreamReader xmlr) throws TransformerConfigurationException,
TransformerFactoryConfigurationError, TransformerException
{
Transformer transformer = TransformerFactory.newInstance().newTransformer();
StringWriter stringWriter = new StringWriter();
transformer.transform(new StAXSource(xmlr), new StreamResult(stringWriter));
return stringWriter.toString();
}
I'm using Jackson mapper version 2.6.5 with Spring Boot but I can seem to get SerializationFeature.INDENT_OUTPUT to work. I'm following the tutorial here. My code is as follows.
public class SerializationExampleTreeModel {
public static void main(String[] args) throws IOException {
// Create the node factory that gives us nodes.
JsonNodeFactory nodeFactory = new JsonNodeFactory(false);
StringWriter stringWriter = new StringWriter();
// create a json factory to write the treenode as json. for the example
// we just write to console
JsonFactory jsonFactory = new JsonFactory();
JsonGenerator generator = jsonFactory.createGenerator(stringWriter);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
// the root node - album
JsonNode album = nodeFactory.objectNode();
album.put("Album-Title", "Kind Of Blue")
ArrayNode songs = nodeFactory.arrayNode()
songs.add("Song8").add("Song2")
album.put("Songs", songs)
ObjectNode artist = nodeFactory.objectNode()
artist.put("Name", "Alex" )
album.put( "artist", artist)
mapper.writeTree(generator, album)
println stringWriter.toString()
}
}
I always get the result:
{"Album-Title":"Kind Of Blue","Songs":["Song8","Song2"],"artist":{"Name":"Alex"}} whether I include the line mapper.configure(SerializationFeature.INDENT_OUTPUT, true) or not. What is going on?
Note: I'm compiling my code using groovyc and semi-colons aren't required.
The problem is you are using StringWriter to write the output and it ignores the formatting you set on ObjectMapper as expected. Instead, use:
System.out.println(mapper.writeValueAsString(album));
If you prefer to use as you are using, you can declare the printer like this before writing the tree:
generator.setPrettyPrinter(new DefaultPrettyPrinter());
mapper.writeTree(generator, album);
This will allow the correct output with:
stringWriter.toString()
I am working on a android(2.2) project which needs xsl transformation. The below code works perfectly in a regular non-android java project
public static String transform() throws TransformerException {
Source xmlInput = new StreamSource(new File("samplexml.xml"));
Source xslInput = new StreamSource(new File("samplexslt.xslt"));
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xslInput);
OutputStream baos = new ByteArrayOutputStream();
Result result = new StreamResult(baos);
transformer.transform(xmlInput, result);
return baos.toString();
}
I need similar functionality on android. For this I created 2 files under resources/raw:
samplexml.xml
samplexslt.xslt
(contents of these files come from here.
I tried the below code & it does not work (note the StreamSource constructor arg):
public static String transform() throws TransformerException {
TransformerFactory factory = TransformerFactory.newInstance();
Source xmlInput = new StreamSource(this.getResources().openRawResource(R.raw.samplexml));
Source xslInput = new StreamSource(this.getResources().openRawResource(R.raw.samplexslt));
Transformer transformer = factory.newTransformer(xslInput);//NullPointerException here
OutputStream baos = new ByteArrayOutputStream();
Result result = new StreamResult(baos);
transformer.transform(xmlInput, result);
}
I saw the spec & believe I need to set a systemId. But I couldn't get the above code to work.
So, in an android project, how to handle xslt transformations? Please provide your thoughts.
As we know that we Cannot usethisin a static context and you are doing this in your static method transform(). You can do it like this_
public class YourLoadXSLClass extends Activity {
static Resources res;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
res = getResources();
String strHTML = transform();
// Other code.....
}
/*
* Your method that Transform CSLT.
*/
public static String transform() throws TransformerException {
TransformerFactory factory = TransformerFactory.newInstance();
// Now your raw files are accessible here.
Source xmlInput = new StreamSource(
LoadXSLTinWebview.res.openRawResource(R.raw.samplexml));
Source xslInput = new StreamSource(
LoadXSLTinWebview.res.openRawResource(R.raw.samplexslt));
Transformer transformer = factory.newTransformer(xslInput);
OutputStream baos = new ByteArrayOutputStream();
Result result = new StreamResult(baos);
transformer.transform(xmlInput, result);
return baos.toString();
}
}
Here is the complete class code that do the needful. I hope this will help you & all!
I've never done anything with XSLT but, looking at your code, logically there are only two things that could cause an NPE on that line. The first would be that factory might be null but that doesn't make sense.
That leaves xslInput as being the culprit which suggests openRawResource(R.raw.samplexslt) is failing to return a valid InputStream for the StreamSource constructor to use. Try putting a log statement in such as...
if (xslInput != null {
Transformer transformer = factory.newTransformer(xslInput);
...
}
else
Log.d("SomeTAG", "xslInput is null!!!");
If it turns out that xslInput is actually null then it suggests openRawResource(...) can't find/process the .xslt file properly. In that case I'd suggest using AssetManagerto open the .xslt file by name...
AssetManager am = this.getAssets();
Source xslInput = new StreamSource(am.open("samplexslt.xslt"));
Is there a Logger that will easily log my stacktrace (what I get with
ex.printStackTrace())? I've searched the log4j docs and found nothing
about logging the stacktrace.
I can do this myself with
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
String stacktrace = sw.toString();
logger.error(stacktrace);
but I don't want to duplicate this code all over the place.
If log4j won't do this for me is there another logging package that will
log the stacktrace for me?
Thanks.
Using log4j this is done with:
logger.error("An error occurred", exception);
The first argument is a message to be displayed, the second is the exception (throwable) whose stacktrace is logged.
Another option is commons-logging, where it's the same:
log.error("Message", exception);
With java.util.logging this can be done via:
logger.log(Level.SEVERE, "Message", exception);
In java.util.logging you can initialize the logger with custom log formatter like here:
private Logger initTextLogger() throws SecurityException, IOException {
Logger logger = Logger.getLogger(YourClass.class.getName());
FileHandler fileHandler = new FileHandler(logFilePath, false);
SimpleFormatter logFormatter = new SimpleFormatter() {
#Override
public String format(LogRecord record) {
String stacktrace = "";
Throwable t = record.getThrown();
if(t!=null){
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
stacktrace = sw.toString();
}
return record.getLevel() + ": " + record.getMessage() + "\r\n"
+ stacktrace;
}
};
fileHandler.setFormatter(logFormatter);
logger.addHandler(fileHandler);
return logger;
}