I want to get a current SlingRequest.getRequestURI() and pass it to new LinkTransformer(links, repository, <<requestedUrl>>). So I tried to add an annotation #Reference SlingRequest slingRequest and it throws me exception. Here is my code:
package com.my.transformer;
import com.my.transformer.impl.LinkTransformer;
import org.apache.felix.scr.annotations.*;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.rewriter.Transformer;
import org.apache.sling.rewriter.TransformerFactory;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.RepositoryException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
#Service(value = TransformerFactory.class)
#Component(immediate = true, metatype = true,
label="My Link Rewriter",
name= "com.my.transformer.LinkTransformerFactory",
description="Rewrite certain links")
public class LinkTransformerFactory implements TransformerFactory {
private static final Logger log = LoggerFactory.getLogger(LinkTransformerFactory.class);
#Property(
value = "mylinktransformer",
propertyPrivate = true)
static final String PIPELINE_TYPE = "pipeline.type";
#Property(unbounded= PropertyUnbounded.ARRAY,description="....")
private static final String LINKS = "links";
private ArrayList<String> links;
#Reference
SlingRepository repository;
#Activate
protected void activate(ComponentContext context)
throws RepositoryException {
final Dictionary<?, ?> properties = context.getProperties();
String[] prop = (String[])properties.get(LINKS);
links = new ArrayList<String>(Arrays.asList(prop));
log.info("LinkTransformerFactory.activate");
}
#Override
public Transformer createTransformer() {
return new LinkTransformer(links, repository);
}
}
Thank you
You cannot obtain it in the TransformerFactory, but you don't need it to give it to the LinkTransformer. The Transformer will receive it in the init method.
The init method of Transformer receives a ProcessingContext, you can use ProcessingContext.getRequest() to get it
Related
I'm working with a remote API which can change and I have no control over it.
This is why I have written myself a small jackson module that has a DeserializationProblemHandler implementation which logs those "unknown properties".
This works just fine, but I want to make my life easier and log the actual received JSON in the logs as well.
So having the following JSON example:
{ "myUnknownProperty": "value123", "myIgnoredProperty": true, "myKnownProperty": true }
A log should look something similar to this:
WARN: Found unexpected property while parsing json: /myUnknownProperty (type: com.foo.PartiallyIgnoredPOJO); JSON { "myUnknownProperty": "value123", "myIgnoredProperty": true, "myKnownProperty": true }
Here is the test case I currently have:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.IOException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class ExampleTest
{
// Test
#Test
public void mockTest() throws JsonProcessingException
{
Logger logger = mock(Logger.class);
try
{
new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.registerModule(new UnknownPropertyModule(logger))
.readValue(partiallyIgnoredSONSample, PartiallyIgnoredPOJO.class);
}
finally
{
verify(logger).warn(UnknownPropertyModule.MESSAGE,
"/myUnknownProperty",
PartiallyIgnoredPOJO.class,
partiallyIgnoredSONSample);
}
}
// Implementations
private static class UnknownPropertyDeserializationProblemHandler extends DeserializationProblemHandler
{
private final Logger logger;
private final String format;
UnknownPropertyDeserializationProblemHandler(final Logger logger, final String format)
{
this.logger = logger;
this.format = format;
}
#Override
public boolean handleUnknownProperty(final DeserializationContext context,
final JsonParser parser,
final JsonDeserializer<?> deserializer,
final Object beanOrClass,
final String propertyName) throws IOException
{
final Class<?> type = beanOrClass.getClass();
String jsonPath = parser.getParsingContext().pathAsPointer().toString();
String json = "__How_to_get_the_entire_JSON_from_the_root___";
logger.warn(format, jsonPath, type, json);
return false;
}
}
private static class UnknownPropertyModule extends Module
{
public static final String MESSAGE = "Found unexpected property while parsing json: {} (type: {}); JSON: {}";
private final Logger logger;
public UnknownPropertyModule(#Nullable final Logger logger)
{
this.logger = logger != null ? logger : LoggerFactory.getLogger(getClass().getName());
}
#Override
public String getModuleName()
{
return getClass().getSimpleName();
}
#Override
public Version version()
{
return Version.unknownVersion();
}
#Override
public void setupModule(Module.SetupContext context)
{
context.addDeserializationProblemHandler(new UnknownPropertyDeserializationProblemHandler(logger, MESSAGE));
}
}
// Example json & mapping.
private static final String partiallyIgnoredSONSample = "{ \"myUnknownProperty\": \"value123\", \"myIgnoredProperty\": true, \"myKnownProperty\": true }";
#JsonIgnoreProperties("myIgnoredProperty")
private static class PartiallyIgnoredPOJO
{
private final boolean myKnownProperty;
#JsonCreator
public PartiallyIgnoredPOJO(#JsonProperty("myKnownProperty") boolean myKnownProperty)
{
this.myKnownProperty = myKnownProperty;
}
public boolean getMyKnownProperty()
{
return myKnownProperty;
}
}
}
From what I've seen in the debugger, you could use parser.getParsingContext().getParent() to get to the json root, but I'm unsure how to get the actual buffered data. Any ideas?
I'm trying to unit test the method responsible for adding to map categorized books.
#Service
public class BookService {
private final List<BookServiceSource> sources;
#Autowired
public BookService(List<BookServiceSource> sources) {
this.sources = sources;
}
public Map<Bookstore, List<Book>> getBooksByCategory(CategoryType category) {
return sources.stream()
.collect(Collectors.toMap(BookServiceSource::getName,
source -> source.getBooksByCategory(category)));
}
}
BookSerivceSource is an interface. This interface is implemented by two classes. I'm gonna provide just one, as the second is really similiar.
EmpikSource (one of implementation)
package bookstore.scraper.book.booksource.empik;
import bookstore.scraper.book.Book;
import bookstore.scraper.book.booksource.BookServiceSource;
import bookstore.scraper.enums.Bookstore;
import bookstore.scraper.enums.CategoryType;
import bookstore.scraper.urlproperties.EmpikUrlProperties;
import bookstore.scraper.utilities.JSoupConnector;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.IntStream;
#Service
public class EmpikSource implements BookServiceSource {
private static final int FIRST_PART_PRICE = 0;
private static final int SECOND_PART_PRICE = 1;
private static final int BESTSELLERS_NUMBER_TO_FETCH = 5;
private static final int CATEGORIZED_BOOKS_NUMBER_TO_FETCH = 15;
private static final String DIV_PRODUCT_WRAPPER = "div.productWrapper";
private static final String DATA_PRODUCT_ID = "data-product-id";
private final EmpikUrlProperties empikUrlProperties;
private final JSoupConnector jSoupConnector;
private Map<CategoryType, String> categoryToEmpikURL;
#Autowired
public EmpikSource(EmpikUrlProperties empikUrlProperties, JSoupConnector jSoupConnector) {
this.empikUrlProperties = empikUrlProperties;
this.jSoupConnector = jSoupConnector;
categoryToEmpikURL = createCategoryToEmpikURLMap();
}
#Override
public Bookstore getName() {
return Bookstore.EMPIK;
}
#Override
public List<Book> getBooksByCategory(CategoryType categoryType) {
Document document = jSoupConnector.connect(categoryToEmpikURL.get(categoryType));
List<Book> books = new ArrayList<>();
List<Element> siteElements = document.select("div.productBox__info");
IntStream.range(0, CATEGORIZED_BOOKS_NUMBER_TO_FETCH)
.forEach(iteratedElement -> {
String author = executeFetchingAuthorProcess(siteElements, iteratedElement);
String price = convertEmpikPriceWithPossibleDiscountToActualPrice(siteElements.get(iteratedElement).select("div.productBox__price").first().text());
String title = siteElements.get(iteratedElement).select("span").first().ownText();
String productID = siteElements.get(iteratedElement).select("a").first().attr(DATA_PRODUCT_ID);
String bookUrl = createBookURL(title, productID);
books.add(Book.builder()
.author(author)
.price(price)
.title(title)
.productID(productID)
.bookURL(bookUrl)
.build());
});
return books;
}
private Map<CategoryType, String> createCategoryToEmpikURLMap() {
Map<CategoryType, String> map = new EnumMap<>(CategoryType.class);
map.put(CategoryType.CRIME, empikUrlProperties.getCrime());
map.put(CategoryType.BESTSELLER, empikUrlProperties.getBestSellers());
map.put(CategoryType.BIOGRAPHY, empikUrlProperties.getBiographies());
map.put(CategoryType.FANTASY, empikUrlProperties.getFantasy());
map.put(CategoryType.GUIDES, empikUrlProperties.getGuides());
map.put(CategoryType.MOST_PRECISE_BOOK, empikUrlProperties.getMostPreciseBook());
map.put(CategoryType.ROMANCES, empikUrlProperties.getRomances());
return map;
}
private String convertEmpikPriceWithPossibleDiscountToActualPrice(String price) {
String[] splittedElements = price.split("\\s+");
return splittedElements[FIRST_PART_PRICE] + splittedElements[SECOND_PART_PRICE];
}
private String createBookURL(String title, String productID) {
return String.format(empikUrlProperties.getConcreteBook(), title, productID);
}
//method is required as on empik site, sometimes occurs null for author and we need to change code for fetching
private static String executeFetchingAuthorProcess(List<Element> siteElements, int i) {
String author;
Element authorElements = siteElements.get(i).select("span > a").first();
if (authorElements != null)
author = authorElements.ownText();
else
author = siteElements.get(i).select("> span > span").first().text();
return author;
}
private String concatUrlWithTitle(String url, String title) {
return String.format(url, title);
}
}
JsoupConnector:
package bookstore.scraper.utilities;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.stereotype.Component;
import java.io.IOException;
#Component
public class JSoupConnector {
public Document connect(String url) {
try {
return Jsoup.connect(url).get();
} catch (IOException e) {
throw new IllegalArgumentException("Cannot connect to" + url);
}
}
}
Properties class:
package bookstore.scraper.urlproperties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
#Getter
#Setter
#Component
#ConfigurationProperties("external.library.url.empik")
public class EmpikUrlProperties {
private String mostPreciseBook;
private String bestSellers;
private String concreteBook;
private String romances;
private String biographies;
private String crime;
private String guides;
private String fantasy;
}
While debugging the test I see that the sources size is 0. How should I add mocked object to the sources list or could you tell me if there is better way to do this?
//EDIT
Forgot to paste the test :P
Test
package bookstore.scraper.book;
import bookstore.scraper.book.booksource.BookServiceSource;
import bookstore.scraper.book.booksource.empik.EmpikSource;
import bookstore.scraper.book.booksource.merlin.MerlinSource;
import bookstore.scraper.dataprovider.EmpikBookProvider;
import bookstore.scraper.dataprovider.MerlinBookProvider;
import bookstore.scraper.enums.Bookstore;
import bookstore.scraper.enums.CategoryType;
import bookstore.scraper.urlproperties.EmpikUrlProperties;
import bookstore.scraper.urlproperties.MerlinUrlProperties;
import bookstore.scraper.utilities.JSoupConnector;
import org.jsoup.nodes.Document;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.List;
import java.util.Map;
import static bookstore.scraper.dataprovider.MergedBestsellersMapProvider.prepareExpectedMergedBestSellerMap;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class BookServiceTest {
#Mock
MerlinSource merlinSource;
#Mock
EmpikSource empikSource;
#Mock
BookServiceSource bookServiceSource;
#Mock
private EmpikUrlProperties empikMock;
#Mock
private MerlinUrlProperties merlinMock;
#Mock
JSoupConnector jSoupConnector;
#Mock
List<BookServiceSource> source;
#InjectMocks
BookService bookService;
#Test
public void getBooksByCategory() {
List<Book> merlinBestsellers = MerlinBookProvider.prepare5Bestsellers();
List<Book> empikBestsellers = EmpikBookProvider.prepare5Bestsellers();
Document empikDocument = mock(Document.class);
Document merlinDocument = mock(Document.class);
source.add(empikSource);
source.add(merlinSource);
when(bookServiceSource.getName()).thenReturn(Bookstore.EMPIK);
when(jSoupConnector.connect("https://www.empik.com/bestsellery/ksiazki")).thenReturn(empikDocument);
when(empikMock.getBestSellers()).thenReturn("https://www.empik.com/bestsellery/ksiazki");
when(empikSource.getBooksByCategory(CategoryType.CRIME)).thenReturn(empikBestsellers);
when(bookServiceSource.getName()).thenReturn(Bookstore.MERLIN);
when(jSoupConnector.connect("https://merlin.pl/bestseller/?option_80=10349074")).thenReturn(merlinDocument);
when(merlinMock.getBestSellers()).thenReturn("https://merlin.pl/bestseller/?option_80=10349074");
when(merlinSource.getBooksByCategory(CategoryType.CRIME)).thenReturn(merlinBestsellers);
Map<Bookstore, List<Book>> actualMap = bookService.getBooksByCategory(CategoryType.CRIME);
Map<Bookstore, List<Book>> expectedMap = prepareExpectedMergedBestSellerMap();
assertEquals(expectedMap, actualMap);
}
}
As mentioned before do not try to mock the List object.
Also generally avoid to create mocks for objects that you can simply create on your own and try to restrict yourself to mock only dependencies.
A simplified version of your test could look like this:
As your test covers quite a bit more than the Unit BookService
I decided to minimize it for this example.
You might want to do all the other stuff in a test for the specific implementation instead.
#Test
public void getBooksByCategory() {
List<Book> empikBestsellers = EmpikBookProvider.prepare5Bestsellers();
List<Book> merlinBestsellers = MerlinBookProvider.prepare5Bestsellers();
BookServiceSource bookServiceSource1 = Mockito.mock(BookServiceSource.class);
Mockito.when(bookServiceSource1.getName()).thenReturn(Bookstore.EMPIK);
Mockito.when(bookServiceSource1.getBooksByCategory(CategoryType.CRIME)).thenReturn(empikBestsellers);
BookServiceSource bookServiceSource2 = Mockito.mock(BookServiceSource.class);
Mockito.when(bookServiceSource2.getName()).thenReturn(Bookstore.MERLIN);
Mockito.when(bookServiceSource2.getBooksByCategory(CategoryType.CRIME)).thenReturn(merlinBestsellers);
List<BookServiceSource> sources = new ArrayList<>();
sources.add(bookServiceSource1);
sources.add(bookServiceSource2);
BookService service = new BookService(sources);
Map<Bookstore, List<Book>> actualMap = service.getBooksByCategory(CategoryType.CRIME);
// compare result
}
I don't believe you should be mocking the list of BookServiceSource since your adds will do nothing since it is not a real list.
This answer here should provide the information you are looking for: Mockito - Injecting a List of mocks
Edit for more clarity:
#InjectMocks should not be used if you can help it, it has a tendency to fail silently.
The other point I was attempting to make is that you are using a mocked list, and because of that when it is told to add elements it will not.
There are two solutions to the problem that you can use. Firstly you could create a when thenreturn for the stream of BookServiceSources, not the recommended solution.
Secondly what would be better is to create a testSetup method making use of the #Before annotation to create the BookService.
#Before
public void testSetup(){
List<BookServiceSource> list = new LinkedList<>();
list.add(merlinSource);
list.add(empikSource);
bookService = new BookService(list);
}
Try #Spy. It allows you to inject actual instance of a list that you have initialized by yourself and which also can be mocked partially.
#Spy
private List<BookServiceSource> sources = new ArrayList<>();
It seems that you have used different name for the List, prefer to use the smae name that field to mock is injected is; sources.
Good explanation here.
5. Mock vs. Spy in Mockito :
When Mockito creates a mock – it does so from the Class of a Type, not from an actual instance. The mock simply creates a bare-bones shell instance of the Class, entirely instrumented to track interactions with it.
On the other hand, the spy will wrap an existing instance. It will still behave in the same way as the normal instance – the only difference is that it will also be instrumented to track all the interactions with it.
I have a problem with unmarshaling xml. When I unmarshal an XML that hasn't got any attributes it works just fine but when I try to unmarshal an XML that has an attribute in any element I get only null values.
Here is my code:
<Document xmlns="sd">
<CstmrCdtTrfInitn>
<GrpHdr>
<NbOfTxs>2</NbOfTxs>
<CtrlSum>100</CtrlSum>
<blabla>bla</blabla>
</GrpHdr>
<PmtInf>
<NbOfTxs>2</NbOfTxs>
<CtrlSum>100</CtrlSum>
<blabla>bla</blabla>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
The xml schema:
package XMLElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement(name = "GrpHdr")
#XmlAccessorType(XmlAccessType.FIELD)
public class GrpHdr {
#XmlElement(name = "MsgId")
private String msgId;
#XmlElement(name = "CreDtTm")
private String creDtTm;
#XmlElement(name = "NbOfTxs")
private String nbOfTxs;
#XmlElement(name = "CtrlSum")
private String ctrlSum;
#XmlElement(name = "InitgPty")
private String initgPty;
// MsgId
public String getMsgId() {
return msgId;
}
public void setMsgId(String string_msgId) {
msgId = string_msgId;
}
// CreDtTm
public String getCreDtTm() {
return creDtTm;
}
public void setCreDtTm(String string_creDtTm) {
creDtTm = string_creDtTm;
}
// NbOfTxs
public String getNbOfTxs() {
return nbOfTxs;
}
public void setNbOfTxs(String string_nbOfTxs) {
nbOfTxs = string_nbOfTxs;
}
// CtrlSum
public String getCtrlSum() {
return ctrlSum;
}
public void setCtrlSum(String string_ctrlSum) {
ctrlSum = string_ctrlSum;
}
// InitgPty
public String getInitgPty() {
return initgPty;
}
public void setInitgPty(String string_initgPty) {
initgPty = string_initgPty;
}
}
And the Main.java
package application;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import XMLElement.CstmrCdtTrfInitn;
import XMLElement.Document;
import XMLElement.GrpHdr;
import XMLElement.PmtInf;
public class Main {
public static void main(String[] args) throws Exception {
XMLInputFactory xif2 = XMLInputFactory.newFactory();
StreamSource xml2 = new StreamSource("D:\\test2.xml");
XMLStreamReader xsr2 = xif2.createXMLStreamReader(xml2);
xsr2.nextTag();
while(xsr2.hasNext()) {
if(xsr2.isStartElement() &&
xsr2.getLocalName().equals("GrpHdr")) {
break;
}
xsr2.next();
}
JAXBContext jc2 = JAXBContext.newInstance(GrpHdr.class);
Unmarshaller unmarshaller2 = jc2.createUnmarshaller();
JAXBElement<GrpHdr> jb2 = unmarshaller2.unmarshal(xsr2,
GrpHdr.class);
xsr2.close();
GrpHdr grpHdr = jb2.getValue();
System.out.println(grpHdr.getNbOfTxs());
System.out.println(grpHdr.getCtrlSum());
}
}
When I remove the xmlns="sd" i get the result without any problem, but otherwise I get only null. Any suggestions on what I am doing wrong?
Note that xmlns stands for XML Namespace. So, if you have namespace in your XML document it will going to bind all tags of XML document with that namespace, so, it's obvious that XML unmarshalling won't work because your tag would be say sd:XYZ and JAXB would be checking for XYZ only.
You have to declare namespace in your XML mapping class as well to make this work with namespace (or remove namespace if you don't need it). You can add XmlSchema annotation in your document class to make it work with xmlns attribute.
#XmlSchema(
namespace = "http://www.example.com/namespaceURI",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(prefix="sd", namespaceURI="http://www.example.com/namespaceURI")
}
)
I have a class in which I take all of my Properties, and hide their passwords before logging.
#Override
public void afterPropertiesSet() throws Exception {
Properties loadedProperties = this.mergeProperties();
loadedProperties.entrySet().stream().forEach(singleProperty -> {
String key = singleProperty.getKey().toString();
String value = HIDDEN_VALUE;
if (!Arrays.stream(PASSWORD_PATTERNS).anyMatch(pattern -> key.toLowerCase().contains(pattern))) {
value = singleProperty.getValue().toString();
}
logger.info("LoadedProperty: "+ key +"=" + value);
});
}
I have migrated to log4j2 and would like to test this class, checking the output of log4j2. It currently uses log4j and works, however when I migrated to log4j2, I get
Wanted but not invoked:
mockAppender.append();
-> at com.comp.spmConf.ExceptionLoggerTest.verifyErrorMessages(ExceptionLoggerTest.java:87)
However, there were other interactions with this mock:
mockAppender.getName();
-> at org.apache.logging.log4j.core.config.AbstractConfiguration.addLoggerAppender(AbstractConfiguration.java:675)
mockAppender.getName();
-> at org.apache.logging.log4j.core.config.AppenderControl.(AppenderControl.java:51)
Here is my log4j1 test class:
import org.apache.log4j.Appender;
import org.apache.log4j.LogManager;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.Properties;
#RunWith(MockitoJUnitRunner.class)
public class SpmPropertyTracerTest {
#Mock
private Appender appenderMock;
#Captor
private ArgumentCaptor captorLoggingEvent;
private SpmPropertyTracer tracer;
#Before
public void setup() {
LogManager.getRootLogger().addAppender(appenderMock);
tracer = new SpmPropertyTracer();
}
#After
public void teardown() {
LogManager.getRootLogger().removeAppender(appenderMock);
}
#Test
public void printPropertiesTest() throws Exception{
String key1 = "Foo";
String val1 = "True";
Properties properties = new Properties();
properties.setProperty(key1, val1);
tracer.setProperties(properties);
String expectedString = String.format("LoadedProperty: %s=%s", key1, val1);
tracer.afterPropertiesSet();
Mockito.verify(appenderMock).doAppend((LoggingEvent)captorLoggingEvent.capture());
LoggingEvent loggingEvent = (LoggingEvent) captorLoggingEvent.getValue();
assert expectedString.equals(loggingEvent.getRenderedMessage());
}
}
And here is my log4j2 test class, am I doing something wrong in the log4j to log4j2 migration?
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
#RunWith(MockitoJUnitRunner.class)
public class TestClass {
#Mock
private Appender mockAppender;
#Captor
private ArgumentCaptor<LogEvent> captorLoggingEvent;
private SpmPropertyTracer tracer;
private Logger logger;
private LogEvent logEvent;
#Before
public void setup() {
// prepare the appender so Log4j likes it
when(mockAppender.getName()).thenReturn("MockAppender");
when(mockAppender.isStarted()).thenReturn(true);
when(mockAppender.isStopped()).thenReturn(false);
logger = (Logger)LogManager.getLogger(SpmPropertyTracer.class);
logger.addAppender(mockAppender);
logger.setLevel(Level.INFO);
tracer = new SpmPropertyTracer();
}
#After
public void tearDown() {
// the appender we added will sit in the singleton logger forever
// slowing future things down - so remove it
logger.removeAppender(mockAppender);
}
#Test
public void loggingIsCaptured() throws Exception {
String key1 = "Foo";
String val1 = "True";
Properties properties = new Properties();
properties.setProperty(key1, val1);
tracer.setProperties(properties);
String expectedString = String.format("LoadedProperasdfty: %s=%s", key1, val1);
tracer.afterPropertiesSet();
verifyErrorMessages(expectedString);
}
// handy function to inspect the messages sent to the logger
private void verifyErrorMessages(String ... messages) {
verify(mockAppender, times(messages.length)).append((LogEvent)captorLoggingEvent.capture());
int i=0;
for(LogEvent loggingEvent:captorLoggingEvent.getAllValues()) {
assertEquals(messages[i++], loggingEvent.getMessage().getFormattedMessage());
}
}
The parent project was brining in a log4j dependency, thus slf4j was binding with log4j and not log4j2 and that is why the append method was not invoked. Removing that dependency fixed my errors.
I'm trying to get data using #DataProvider, which returns Object[][], in which I'm pasting value by calling another method. This method, in its turn, using #Parameters to get value from XML.
The problem is that I'm getting NullPointerException, because in #DataProvider calling required method with #Parameters by passing value null, hoping that #Parameters will change this value to appropriate one from XML.
At the same time, I cannot call method by not passing any arguments to it.
Code:
Class TestSuite
package blablabla.mainPackage;
import blablabla.framework.*;
import java.io.IOException;
import java.util.ArrayList;
import org.testng.annotations.Test;
public class TestSuite extends Config {
FilesOperations fOps = new FilesOperations();
List<String> fileNames = new ArrayList<String>();
#Test(groups = {"positive"},
dataProvider = "getRandomFileName",
priority = 1)
public void createFileRandom(String fileName) throws IOException {
fOps.createFile(fileName, tempPath);
fileNames.add(fileName);
}
}
Class Config
package blablabla.mainPackage;
import blablabla.framework.*;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Parameters;
public class Config extends DataProviders{
public static Path tempPath;
DirectoriesOperations dirOps = new DirectoriesOperations();
#BeforeSuite(alwaysRun = true)
#Parameters({"path"})
public void tearUp(String path) throws IOException {
tempPath = dirOps.createTempDir(Paths.get(path));
}
#AfterSuite(alwaysRun = true, enabled = true)
public void tearDown() throws IOException {
dirOps.deleteTempDirOnExit(tempPath);
}
}
Class DataProviders
package blablabla.framework;
import java.io.IOException;
import org.testng.annotations.DataProvider;
public class DataProviders {
FilesOperations fOps = new FilesOperations();
HelpFunctions hf = new HelpFunctions();
ParametrizedFunctions pf = new ParametrizedFunctions();
#DataProvider(name = "getRandomFileName")
public Object[][] getRandomFileName() {
return new Object[][]{new Object[]{pf.generateRandomFileNameWithExtension(null)}};
}
}
Class ParametrizedFunctions
package blablabla.framework;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Parameters;
public class ParametrizedFunctions {
FilesOperations fOps = new FilesOperations();
HelpFunctions hf = new HelpFunctions();
#Parameters({"extensionsArray"})
public String generateRandomFileNameWithExtension(String extensionsArray) {
return fOps.getFileName(hf.stringToArray(extensionsArray), null);
}
}
Here, #Parameters({"extensionsArray"}) does't provide a value from XML file. It just takes null, that was passed as an argument for calling method generateRandomFileNameWithExtension(). And, at the same time, I cannot call this method from #DataProvider without passing any argument to method called.
Hope for your suggestions.
I found the solution. The best way to pass parameter from XML into #DataProvider is passing ITestContext context as the argument to the #DataProvider method, and then, get required parameter using:
context.getCurrentXmlTest().getParameter("parameterName");
Full code of #DataProvider:
#DataProvider(name = "getRandomFileName")
public Object[][] getRandomFileName(ITestContext context) {
String extensionsArray = context.getCurrentXmlTest().getParameter("extensionsArray");
return new Object[][]{new Object[]{pf.generateRandomFileNameWithExtension(extensionsArray)}};
}
Thanks all for your suggestions.