JAX-WS Endpoint bean creation fails with ServiceConstructionException - web-services

I am trying to publish a webservice with spring-boot
Here is how I have set it up.
I have an interface that has some methods say
#WebService
public interface FirstInterface
{
#WebMethod
void method1(#WebParam(name = "id") String id);
void method2(#WebParam(name = "id2") String id);
}
I have another interface that has some more methods
and it extends FirstInterface
#WebService
public interface SecondInterface extends FirstInterface
{
#WebMethod
void method3(#WebParam(name = "id") String id);
void method4(#WebParam(name = "id2") String id);
}
Now I have an implementation class that implements SecondInterface
and has an endpointInterface referring to my SecondInterface something like this:
#Service
#WebService(endpointInterface = "com.somepackage.SecondInterface")
public class CallBackServicesImpl implements SecondInterface
{
#Override
//override all four methods here
}
Now I have a configuration class that is publishing these services
#Configuration
public class WebServiceConfig
{
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), serviceImpl());
endpoint.publish(SERVICE_NAME_PATH);
return endpoint;
}
}
Problem: The webservice gets published with this setup with endpointinterface pointing to FirstInterface but only two methods will be available for use.
Now I all want four methods to be available for the client to use, so I point endpointinterface to SecodInterface and it start throwing exception saying Error creating bean with name 'endpoint',org.apache.cxf.service.factory.ServiceConstructionException
Am I missing something basic here? How can I achieve this behavior?

Remove the #WebService annotation on impl class
Here is the working copy
FirstInterface.java
#WebService
public interface FirstInterface {
#WebMethod
void method1(#WebParam(name = "id") String id);
#WebMethod
void method2(#WebParam(name = "id2") String id);
}
SecondInterface.java
#WebService
public interface SecondInterface extends FirstInterface {
#WebMethod
void method3(#WebParam(name = "id") String id);
#WebMethod
void method4(#WebParam(name = "id2") String id);
}
Impl class
#Service
public class KPImpl implements SecondInterface {
public static final Logger LOG = LoggerFactory.getLogger(KPImpl.class);
#Override
public void method3(String id) {
LOG.info("Method3 {}", id);
}
#Override
public void method4(String id) {
LOG.info("Method4 {}", id);
}
#Override
public void method1(String id) {
LOG.info("Method1 {}", id);
}
#Override
public void method2(String id) {
LOG.info("Method2 {}", id);
}
}
Finally configuration file
#Configuration
public class WsConfiguration {
#Bean
public Server getJaxWsServer(SpringBus bus, KPImpl impl) {
final JaxWsServerFactoryBean serverFctry = new JaxWsServerFactoryBean();
serverFctry.setAddress("/kp");
serverFctry.setServiceBean(impl);
serverFctry.setServiceClass(KPImpl.class);
serverFctry.getFeatures().add(new LoggingFeature());
return serverFctry.create();
}
}

Related

Spring JPA saveAll doesn't work with a List of object made from Hibernate left join

I'm building a list of objects with a query like the following:
#Query(value="SELECT t1.id, t1.field, t2.id FROM table1 t1 left join table2 t2 on t1.commonfield = t2.commonfield", nativeQuery=true)
public List<Record> recordList();
Obviously, I have the proper builder in class Record. Everything compiles fine.
Then, I try to save the list like this:
recordService.saveAll(recordService.recordList());
And it does not do anything... I want to save the value taken from the join (t2.id) in the table1...
What am I doing wrong?
The repository is implemented this way:
Interface RecordService:
public interface RecordService {
public void save(Record record);
public void saveAll(List<Record> lista);
public Record findById(Integer id);
public void deleteById(Integer id);
public List<Record> findAll();
public List<Record> recordList();
}
Class RecordServiceImpl:
#Service
public class RecordServiceImpl implements RecordService{
#Autowired
private RecordDao recordDao;
#Override
#Transactional(readOnly = true)
public List<Record> findAll() {
return (List<Record>) recordDao.findAll();
}
#Override
#Transactional(readOnly = true)
public Record findById(Integer id) {
return recordDao.findById(id).get();
}
#Override
#Transactional
public void save(Record record) {
recordDao.save(record);
}
#Override
#Transactional
public void saveAll(List<Record> lista) {
recordDao.saveAll(lista);
}
#Override
#Transactional
public void deleteById(Integer id) {
recordDao.deleteById(id);
}
#Override
public List<Record> recordList() {
return recordDao.recordList();
}
}
Interface RecordDao:
#Repository
public interface RecordDao extends PagingAndSortingRepository<Record, Integer>{
#Query(value="SELECT t1.id, t1.field, t2.id FROM table1 t1 left join table2 t2 on t1.commonfield = t2.commonfield", nativeQuery=true)
public List<Record> recordList();
}
The object class Record is mapped as Entity:
#Entity
#Table(name="table1")
public class Record implements Serializable{
...
}

Akka Rest Server Jackson ObjectReader and ObjectWriter Initialization

I am using jackson ObjectReader and ObjectWriter object for serialization and deserialization in Akka Rest Server.
I am getting byteString in the request and deserialize it to object. Below is the scala code for it.
val a = objectReader.readValue[java.util.List[Base[Long]]](request.toArray)
Base class is an abstract class and I can have multiple implementation of it
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
property = "impl")
#JsonSubTypes({
#JsonSubTypes.Type(value = A.class, name = "A")
})
public abstract class Base<T> implements Serializable {
private String impl;
private ResponseStatus status;
public String getImpl() {
return impl;
}
public void setImpl(String impl) {
this.impl = impl;
}
public void setStatus(ResponseStatus status) {
this.status = status;
}
public ResponseStatus getStatus() {
return status;
}
public static class ResponseStatus implements Serializable {
private ReturnCode code;
private String msg;
public void setCode(ReturnCode code) {
this.code = code;
}
public void setMsg(String msg) {
this.msg = msg;
}
public ReturnCode getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
}
How ever I have observed that first call for readValue and writeValueAsBytes takes long.
I tried initializing it. But still it is not improving in Akka Execution Context. Anyone know the solution of it? Please help.

ModelMapper and tree structures

I've got a tree-like structure of entities that I'm trying to map to DTOs using ModelMapper. The trick is that I'm trying to break the graph after the first parent.
Following is a example of what I'm trying to do. I've got a Category entity which has a name and a parent Category. I want my DTO to have a link to it's parent but don't want the latter to have it's own parent (so that I don't get the whole hierarchy)
The trouble is that ModelMapper does not map my DTO's parent because it's of a different type; I declared it as being a DTO rather that a CategoryDTO in order to break the recursion.
If I DO declare my parent as a CategoryDTO, the mapping works fine but I will get all of my parents and grand parents (which I do not want).
Anybody knows how I could fix this?
import java.util.UUID;
import org.junit.Assert;
import org.junit.Test;
import org.modelmapper.ModelMapper;
public class CategoryTest {
public static class Category {
private String uid = UUID.randomUUID().toString();
private String name;
private Category parent;
public Category () {}
public Category(String name, Category parent) {
this.name = name;
this.parent = parent;
}
public String getUid() {return uid;}
public void setUid(String uid) {this.uid = uid;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Category getParent() {return parent;}
public void setParent(Category parent) {this.parent = parent;}
}
public static class DTO {
private String uid;
private String name;
public String getUid() {return uid;}
public void setUid(String uid) {this.uid = uid;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
public static class CategoryDTO extends DTO {
private DTO parent;
public DTO getParent() {return parent;}
public void setParent(DTO parent) {this.parent = parent;}
}
#Test
public void simpleTest() {
Category dto = new Category("Test1",null);
CategoryDTO entity = new ModelMapper().map(dto, CategoryDTO.class);
Assert.assertEquals("Test1", entity.getName());
Assert.assertEquals(dto.getUid(), entity.getUid());
}
#Test
public void withParentTest() {
Category dto = new Category("child",new Category("root", null));
CategoryDTO entity = new ModelMapper().map(dto, CategoryDTO.class);
Assert.assertEquals("child", entity.getName());
Assert.assertEquals(dto.getUid(), entity.getUid());
Assert.assertNotNull(entity.getParent());
}
}
I've managed to solve my problem by customizing my ModelMapper with a Converter. My ModelMapper is now fetched by calling this method:
private ModelMapper getModelMapper() {
ModelMapper map = new ModelMapper();
map.addConverter(new AbstractConverter<Category, AbstractDTO>() {
#Override
protected AbstractDTO convert(Category source) {
if (source == null) return null;
AbstractDTO dto = new AbstractDTO();
dto.setUid(source.getUid());
dto.setName(source.getName());
return dto;
}
});
return map;
}

How to use Moq to Mock an internal class protected method?

I have classes below:
public class TestService
{
public void Upload()
{
var manager = new Manager();
var worker = manager.CreateWorker();
worker.DoWork();
}
}
public class Manager
{
public Worker CreateWork()
{
return new Worker();
}
}
public class Worker()
{
public void DoWork()
{
SetupEnvironment();
//some codes here...
}
protected virtual void SetupEnvironment()
{
//some codes here ...
}
}
I want to use Moq to create an unit test for Upload() method in TestService class, and SetupEnvironment() method of Worker should be mock up. I tried to the code below, but It doesnt' work:
[TestMethod]
public void Upload_Test()
{
var mockWorker = new Mock<Worker>();
mockWorker .Protected().Setup<string>("SetupEnvironment").Returns(() =>
{
//some dummy code here ...
});
var service = new TestService();
service.Upload();
Assert.IsTrue(true);
}
Anybody knows how to make the above unit test code work?
some things need to be changed. See the code of the class TestService and Worker below.
public class TestService
{
private readonly Manager _manager;
public TestService(Manager manager)
{
_manager = manager;
}
public void Upload()
{
var worker = _manager.CreateWorker();
worker.DoWork();
}
}
public class Manager
{
public virtual Worker CreateWorker()
{
return new Worker();
}
}
public class Worker
{
public void DoWork()
{
SetupEnvironment();
//some codes here...
}
protected virtual void SetupEnvironment()
{
//some codes here ...
}
}
Then the test could look like this. The method SetupEnvironment is void so the setup can't return any value and you need to mock the Manager as well and pass it to the TestService e.g. via constructor injection so you can inject your mock-object. HTH
[TestMethod]
public void Upload_Test()
{
bool setupEnvironmentWasCalled = false;
Mock<Worker> mockWorker = new Mock<Worker>();
mockWorker.Protected().Setup("SetupEnvironment")
.Callback(() =>
{
//some dummy code here ...
setupEnvironmentWasCalled = true;
});
Mock<Manager> mockManager = new Mock<Manager>();
mockManager.Setup(m => m.CreateWorker())
.Returns(mockWorker.Object);
var service = new TestService(mockManager.Object);
service.Upload();
Assert.IsTrue(setupEnvironmentWasCalled);
}

What is the best way to unit-test SLF4J log messages?

I'm using slf4j and I want to unit test my code to make sure that warn/error log messages are generated under certain conditions. I'd rather these be strict unit tests, so I'd prefer not to have to pull up logging configuration from a file in order to test that the log messages are generated. The mocking framework I'm using is Mockito.
Create a test rule:
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.stream.Collectors;
public class LoggerRule implements TestRule {
private final ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
private final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
setup();
base.evaluate();
teardown();
}
};
}
private void setup() {
logger.addAppender(listAppender);
listAppender.start();
}
private void teardown() {
listAppender.stop();
listAppender.list.clear();
logger.detachAppender(listAppender);
}
public List<String> getMessages() {
return listAppender.list.stream().map(e -> e.getMessage()).collect(Collectors.toList());
}
public List<String> getFormattedMessages() {
return listAppender.list.stream().map(e -> e.getFormattedMessage()).collect(Collectors.toList());
}
}
Then use it:
#Rule
public final LoggerRule loggerRule = new LoggerRule();
#Test
public void yourTest() {
// ...
assertThat(loggerRule.getFormattedMessages().size()).isEqualTo(2);
}
----- JUnit 5 with Extension Oct 2021 -----
LogCapture:
public class LogCapture {
private ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
LogCapture() {
}
public String getFirstFormattedMessage() {
return getFormattedMessageAt(0);
}
public String getLastFormattedMessage() {
return getFormattedMessageAt(listAppender.list.size() - 1);
}
public String getFormattedMessageAt(int index) {
return getLoggingEventAt(index).getFormattedMessage();
}
public LoggingEvent getLoggingEvent() {
return getLoggingEventAt(0);
}
public LoggingEvent getLoggingEventAt(int index) {
return (LoggingEvent) listAppender.list.get(index);
}
public List<LoggingEvent> getLoggingEvents() {
return listAppender.list.stream().map(e -> (LoggingEvent) e).collect(Collectors.toList());
}
public void setLogFilter(Level logLevel) {
listAppender.clearAllFilters();
listAppender.addFilter(buildLevelFilter(logLevel));
}
public void clear() {
listAppender.list.clear();
}
void start() {
setLogFilter(Level.INFO);
listAppender.start();
}
void stop() {
if (listAppender == null) {
return;
}
listAppender.stop();
listAppender.list.clear();
listAppender = null;
}
ListAppender<ILoggingEvent> getListAppender() {
return listAppender;
}
private Filter<ILoggingEvent> buildLevelFilter(Level logLevel) {
LevelFilter levelFilter = new LevelFilter();
levelFilter.setLevel(logLevel);
levelFilter.setOnMismatch(FilterReply.DENY);
levelFilter.start();
return levelFilter;
}
}
LogCaptureExtension:
public class LogCaptureExtension implements ParameterResolver, AfterTestExecutionCallback {
private Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
private LogCapture logCapture;
#Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType() == LogCapture.class;
}
#Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
logCapture = new LogCapture();
setup();
return logCapture;
}
#Override
public void afterTestExecution(ExtensionContext context) {
teardown();
}
private void setup() {
logger.addAppender(logCapture.getListAppender());
logCapture.start();
}
private void teardown() {
if (logCapture == null || logger == null) {
return;
}
logger.detachAndStopAllAppenders();
logCapture.stop();
}
}
then use it:
#ExtendWith(LogCaptureExtension.class)
public class SomeTest {
#Test
public void sometest(LogCapture logCapture) {
// do test here
assertThat(logCapture.getLoggingEvents()).isEmpty();
}
// ...
}
For testing slf4j without relying on a specific implementation (such as log4j), you can provide your own slf4j logging implementation as described in this SLF4J FAQ. Your implementation can record the messages that were logged and then be interrogated by your unit tests for validation.
The slf4j-test package does exactly this. It's an in-memory slf4j logging implementation that provides methods for retrieving logged messages.
I think you could solve your problem with a custom appender. Create a test appender which implements the org.apache.log4j.Appender, and set your appender in the log4j.properties and load it when you execute test cases.
If you call back to the test harness from that appender you can check the logged messages
For JUnit 5, create an extension that implements the solution provided by andrew-feng above in Create a test rule:
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.stream.Collectors;
public class LoggerExtension implements BeforeEachCallback, AfterEachCallback {
private final ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
private final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
#Override
public void afterEach(ExtensionContext extensionContext) throws Exception {
listAppender.stop();
listAppender.list.clear();
logger.detachAppender(listAppender);
}
#Override
public void beforeEach(ExtensionContext extensionContext) throws Exception {
logger.addAppender(listAppender);
listAppender.start();
}
public List<String> getMessages() {
return listAppender.list.stream().map(e -> e.getMessage()).collect(Collectors.toList());
}
public List<String> getFormattedMessages() {
return listAppender.list.stream().map(e -> e.getFormattedMessage()).collect(Collectors.toList());
}
}
Then use it:
#RegisterExtension
public LoggerExtension loggerExtension = new LoggerExtension();
#Test
public void yourTest() {
// ...
assertThat(loggerExtension.getFormattedMessages().size()).isEqualTo(2);
}
A better test implementation of SLF4J that works really well in an environment with concurrent test execution is https://github.com/portingle/slf4jtesting
I've chimed in on a few discussion on slf4j log testing and the limitations of existing test approaches when it comes to concurrent test execution.
I decided to put my words into code and that git repo is the result.
Instead of mocking SLF4J you could place the important logging calls you need to test inside their own methods which you can mock more easily.
If you really want to mock SLF4J, I would bet you could create your own provider for it that would allow you to supply a mock logger from the SLF4J side instead of injecting one in your service objects.
Using slf4j-test can remove lot of workarounds discussed above
pom.xml
<dependency>
<groupId>uk.org.lidalia</groupId>
<artifactId>slf4j-test</artifactId>
<version>1.2.0</version>
</dependency>
Sample class
#Slf4j
public class SampleClass {
public void logDetails(){
log.info("Logging");
}
}
TestClass
import org.junit.Test;
import uk.org.lidalia.slf4jtest.TestLogger;
import uk.org.lidalia.slf4jtest.TestLoggerFactory;
import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static uk.org.lidalia.slf4jtest.LoggingEvent.info;
public class SampleClassTest {
TestLogger logger = TestLoggerFactory.getTestLogger(SampleClass.class);
#Test
public void testLogging(){
SampleClass sampleClass = new SampleClass();
//Invoke slf4j logger
sampleClass.logDetails();
assertThat(logger.getLoggingEvents(), is(asList(info("Logging"))));
}
}
Refer http://projects.lidalia.org.uk/slf4j-test/ for more details
Similar to #Zsolt, you can mock log4j Appender and set it on the Logger, then verify calls to Appender.doAppend(). This allows you to test without having to modify the real code.
Just use plain Mockito and some reflection logic to mock it:
// Mock the Logger
Logger mock = Mockito.mock(Logger.class);
// Set the Logger to the class you want to test.
// Since this is often a private static field you have to
// hack a little bit: (Solution taken from https://stackoverflow.com/a/3301720/812093)
setFinalStatic(ClassBeeingTested.class.getDeclaredField("log"), mock);
with setFinalStatic method beeing
public static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
Then just execute the to be tested code and verify - e.g. the following verifies that the Logger.warn method was called twice:
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
Mockito.verify(mock,Mockito.atLeastOnce()).warn(argumentCaptor.capture());
List<String> allValues = argumentCaptor.getAllValues();
assertEquals(2, allValues.size());
assertEquals("myFirstExpectedMessage", allValues.get(0));
assertEquals("mySecondExpectedMessage", allValues.get(1));
Please note that setting the final fields via reflection does not work in all cases. I was for example not able to get it working if multiple testcases were trying to modify it.
You can try another library to support easy mocking slf4j loggers - slf4j-mock, your code cen be look as:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.slf4j.Logger;
#RunWith(MockitoJUnitRunner.class)
public class JUnit4ExampleTest {
private static final String INFO_TEST_MESSAGE = "info log test message from JUnit4";
#Mock
Logger logger;
#InjectMocks
Example sut;
#Test
public void logInfoShouldBeLogged() {
// when
sut.methodWithLogInfo(INFO_TEST_MESSAGE);
// then
Mockito.verify(logger).info(INFO_TEST_MESSAGE);
Mockito.verifyNoMoreInteractions(logger);
}
}
As you see you don't need any special steps, in test code. You need only add dependency to library in your project.
More examples and instructions at:
https://www.simplify4u.org/slf4j-mock/
This solution has been mentioned already before in this groovy answer as well as in this comment, but as I don't see it as an answer itself, adding it here as a community wiki answer.
So JUnit5 solution using logback listappender:
import static org.assertj.core.api.Assertions.assertThat;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
public class LoggingTest {
private final ClassToTest sut = new ClassToTest();
private ListAppender<ILoggingEvent> listAppender;
#BeforeEach
void init() {
final var log = (Logger) LoggerFactory.getLogger(ClassToTest.class);
listAppender = new ListAppender<>();
listAppender.start();
log.addAppender(listAppender);
}
#Test
public void testLogging() {
sut.doSomethingThatLogs()
String message = listAppender.list.get(0).getFormattedMessage();
assertThat(message).contains("this message should be logged");
}
}
I have a new answer that I will post at the top in this post (My "old" answer is still at the bottom of this post) (At the time of writing my "old" answer was a "0", so no harm, no foul! )
Newer answer:
Here is the Gradle Package:
testImplementation 'com.portingle:slf4jtesting:1.1.3'
Maven Link:
https://mvnrepository.com/artifact/com.portingle/slf4jtesting
Germane Code:
(below imports and private method would go in MyTestClass(.java))
import static org.junit.Assert.assertNotNull;
import slf4jtest.LogLevel;
import slf4jtest.Settings;
import slf4jtest.TestLogger;
import slf4jtest.TestLoggerFactory;
#Test
public void myFirstTest() {
org.slf4j.Logger unitTestLogger = this.getUnitTestLogger();
ISomethingToTestObject testItem = new SomethingToTestObject (unitTestLogger);
SomeReturnObject obj = testItem.myMethod("arg1");
assertNotNull(wrapper);
/* now here you would find items in the unitTestLogger */
assertContains(unitTestLogger, LogLevel.DebugLevel, "myMethod was started");
}
// render nicer errors
private void assertContains(TestLogger unitTestLogger, LogLevel logLev, String expected) throws Error {
if (!unitTestLogger.contains(logLev, expected)) {
throw new AssertionError("expected '" + expected + "' but got '" + unitTestLogger.lines() + "'");
}
}
// render nicer errors
private void assertNotContains(TestLogger unitTestLogger, LogLevel logLev, String expected) throws Error {
if (unitTestLogger.contains(logLev, expected)) {
throw new AssertionError("expected absence of '" + expected + "' but got '" + unitTestLogger.lines() + "'");
}
}
private TestLogger getUnitTestLogger() {
TestLoggerFactory loggerFactory = Settings.instance()
.enableAll() // necessary as by default only ErrorLevel is enabled
.buildLogging();
TestLogger returnItem = loggerFactory.getLogger(MyTestClasss.class.getName());
assertNotNull(returnItem);
return returnItem;
}
============================= OLD ANSWER BELOW .. DO NOT USE================
Below is my previous answer. I changed my below code ... to use the above package after I discovered it (the above package).
So here is my method.
First, I allow the logger to be injected. But I provide a default as well:
```java
package com.mycompany.myproject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyCoolClass { //implements IMyCoolClass {
private static final String PROCESS_STARTED = "Process started. (key='%1$s')";
private final Logger logger;
public MyCoolClass() {
this(LoggerFactory.getLogger(MyCoolClass.class));
}
public MyCoolClass(Logger lgr) {
this.logger = lgr;
}
public doSomething(int key)
{
logger.info(String.format(PROCESS_STARTED, key));
/*now go do something */
}
}
Then I wrote a very basic in memory logger
```java
import org.slf4j.Marker;
import java.util.ArrayList;
import java.util.Collection;
public class InMemoryUnitTestLogger implements org.slf4j.Logger {
public Collection<String> informations = new ArrayList<String>();
public Collection<String> errors = new ArrayList<String>();
public Collection<String> traces = new ArrayList<String>();
public Collection<String> debugs = new ArrayList<>();
public Collection<String> warns = new ArrayList<>();
public Collection<String> getInformations() {
return informations;
}
public Collection<String> getErrors() {
return errors;
}
public Collection<String> getTraces() {
return traces;
}
public Collection<String> getDebugs() {
return debugs;
}
public Collection<String> getWarns() {
return warns;
}
#Override
public String getName() {
return "FakeLoggerName";
}
#Override
public boolean isTraceEnabled() {
return false;
}
#Override
public boolean isTraceEnabled(Marker marker) {
return false;
}
#Override
public boolean isDebugEnabled() {
return false;
}
#Override
public boolean isDebugEnabled(Marker marker) {
return false;
}
#Override
public boolean isWarnEnabled(Marker marker) {
return false;
}
#Override
public boolean isInfoEnabled(Marker marker) {
return false;
}
#Override
public boolean isWarnEnabled() {
return false;
}
#Override
public boolean isErrorEnabled(Marker marker) {
return false;
}
#Override
public boolean isInfoEnabled() {
return false;
}
#Override
public boolean isErrorEnabled() {
return false;
}
#Override
public void trace(String s) {
this.internalTrace(s);
}
#Override
public void trace(String s, Object o) {
this.internalTrace(s);
}
#Override
public void trace(String s, Object o, Object o1) {
this.internalTrace(s);
}
#Override
public void trace(String s, Object... objects) {
this.internalTrace(s);
}
#Override
public void trace(String s, Throwable throwable) {
this.internalTrace(s);
}
#Override
public void trace(Marker marker, String s) {
this.internalTrace(s);
}
#Override
public void trace(Marker marker, String s, Object o) {
this.internalTrace(s);
}
#Override
public void trace(Marker marker, String s, Object o, Object o1) {
this.internalTrace(s);
}
#Override
public void trace(Marker marker, String s, Object... objects) {
this.internalTrace(s);
}
#Override
public void trace(Marker marker, String s, Throwable throwable) {
this.internalTrace(s);
}
#Override
public void debug(String s) {
this.internalDebug(s);
}
#Override
public void debug(String s, Object o) {
this.internalDebug(s);
}
#Override
public void debug(String s, Object o, Object o1) {
this.internalDebug(s);
}
#Override
public void debug(String s, Object... objects) {
this.internalDebug(s);
}
#Override
public void debug(String s, Throwable throwable) {
this.internalDebug(s);
}
#Override
public void debug(Marker marker, String s) {
this.internalDebug(s);
}
#Override
public void debug(Marker marker, String s, Object o) {
this.internalDebug(s);
}
#Override
public void debug(Marker marker, String s, Object o, Object o1) {
this.internalDebug(s);
}
#Override
public void debug(Marker marker, String s, Object... objects) {
this.internalDebug(s);
}
#Override
public void debug(Marker marker, String s, Throwable throwable) {
this.internalDebug(s);
}
public void info(String s) {
this.internalInfo(s);
}
#Override
public void info(String s, Object o) {
this.internalInfo(s);
}
#Override
public void info(String s, Object o, Object o1) {
this.internalInfo(s);
}
#Override
public void info(String s, Object... objects) {
this.internalInfo(s);
}
#Override
public void info(String s, Throwable throwable) {
this.internalInfo(s);
}
#Override
public void info(Marker marker, String s) {
this.internalInfo(s);
}
#Override
public void info(Marker marker, String s, Object o) {
this.internalInfo(s);
}
#Override
public void info(Marker marker, String s, Object o, Object o1) {
this.internalInfo(s);
}
#Override
public void info(Marker marker, String s, Object... objects) {
this.internalInfo(s);
}
#Override
public void info(Marker marker, String s, Throwable throwable) {
this.internalInfo(s);
}
public void error(String s) {
this.internalError(s);
}
#Override
public void error(String s, Object o) {
this.internalError(s);
}
#Override
public void error(String s, Object o, Object o1) {
this.internalError(s);
}
#Override
public void error(String s, Object... objects) {
this.internalError(s);
}
#Override
public void error(String s, Throwable throwable) {
this.internalError(s);
}
#Override
public void error(Marker marker, String s) {
this.internalError(s);
}
#Override
public void error(Marker marker, String s, Object o) {
this.internalError(s);
}
#Override
public void error(Marker marker, String s, Object o, Object o1) {
this.internalError(s);
}
#Override
public void error(Marker marker, String s, Object... objects) {
this.internalError(s);
}
#Override
public void error(Marker marker, String s, Throwable throwable) {
this.internalError(s);
}
public void warn(String s) {
this.internalWarn(s);
}
#Override
public void warn(String s, Object o) {
this.internalWarn(s);
}
#Override
public void warn(String s, Object... objects) {
this.internalWarn(s);
}
#Override
public void warn(String s, Object o, Object o1) {
this.internalWarn(s);
}
#Override
public void warn(String s, Throwable throwable) {
this.internalWarn(s);
}
#Override
public void warn(Marker marker, String s) {
this.internalWarn(s);
}
#Override
public void warn(Marker marker, String s, Object o) {
this.internalWarn(s);
}
#Override
public void warn(Marker marker, String s, Object o, Object o1) {
this.internalWarn(s);
}
#Override
public void warn(Marker marker, String s, Object... objects) {
this.internalWarn(s);
}
#Override
public void warn(Marker marker, String s, Throwable throwable) {
this.internalWarn(s);
}
private void internalDebug(String s) {
System.out.println(s);
this.debugs.add(s);
}
private void internalInfo(String msg) {
System.out.println(msg);
this.informations.add(msg);
}
private void internalTrace(String msg) {
//??System.out.println(msg);
this.traces.add(msg);
}
private void internalWarn(String msg) {
System.err.println(msg);
this.warns.add(msg);
}
private void internalError(String msg) {
System.err.println(msg);
this.errors.add(msg);
}
Then in my unit tests, I can do one of two things:
private ByteArrayOutputStream setupSimpleLog(Logger lgr) {
ByteArrayOutputStream pipeOut = new ByteArrayOutputStream();
PrintStream pipeIn = new PrintStream(pipeOut);
System.setErr(pipeIn);
return pipeOut;
}
private Logger getSimpleLog() {
Logger lgr = new InMemoryUnitTestLogger();
return lgr;
}
private void myTest() {
Logger lgr = getSimpleLog();
ByteArrayOutputStream pipeOut = this.setupSimpleLog(lgr);
MyCoolClass testClass = new MyCoolClass(lgr);
int myValue = 333;
testClass.doSomething(myValue);
String findMessage = String.format(MyCoolClass.PROCESS_STARTED, myValue);
String output = new String(pipeOut.toByteArray());
assertTrue(output.contains(findMessage));
}
or similar to the above, but do a cast on the custom Logger
private void myTest() {
Logger lgr = getSimpleLog();
MyCoolClass testClass = new MyCoolClass(lgr);
int myValue = 333;
testClass.doSomething(myValue);
String findMessage = String.format(MyCoolClass.PROCESS_STARTED, myValue);
InMemoryUnitTestLogger castLogger = (InMemoryUnitTestLogger)lgr;
/* now check the exact subcollection for the message) */
assertTrue(castLogger.getInfos().contains(findMessage));
}
Take the code with a grain of salt, the ideas are there. I didn't compile the code.
I know it's been a while since this question was posted but I just came across a similar issue and my solution may help. Along the lines of the solution proposed by #Zsolt, we use an appender, more specifically Logback's ListAppender. Showing the code and configurations here (Groovy code but can be easily ported to Java):
Groovy class for log access:
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.spi.LoggingEvent
import ch.qos.logback.core.read.ListAppender
import org.slf4j.LoggerFactory
class LogAccess {
final static String DEFAULT_PACKAGE_DOMAIN = Logger.ROOT_LOGGER_NAME
final static String DEFAULT_APPENDER_NAME = 'LIST'
final List<LoggingEvent> list
LogAccess(String packageDomain = DEFAULT_PACKAGE_DOMAIN, String appenderName = DEFAULT_APPENDER_NAME) {
Logger logger = (Logger) LoggerFactory.getLogger(packageDomain)
ListAppender<LoggingEvent> appender = logger.getAppender(appenderName) as ListAppender<LoggingEvent>
if (appender == null) {
throw new IllegalStateException("'$DEFAULT_APPENDER_NAME' appender not found. Did you forget to add 'logback.xml' to the resources folder?")
}
this.list = appender.list
this.clear()
}
void clear() {
list.clear()
}
boolean contains(String logMessage) {
return list.reverse().any { it.getFormattedMessage() == logMessage }
}
#Override
String toString() {
list.collect { it. getFormattedMessage() }
}
}
Sample logback.xml config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- These 2 'includes' tags ensure regular springboot console logging works as usual -->
<!-- See https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-configure-logback-for-logging -->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<appender name="LIST" class="ch.qos.logback.core.read.ListAppender"/>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="LIST" />
</root>
</configuration>
Test:
LogAccess log = new LogAccess()
def expectedLogEntry = 'Expected Log Entry'
assert !log.contains(expectedLogEntry)
methodUnderTest()
assert log.contains(expectedLogEntry)
I use this in a SpringBoot project with Groovy+Spock, though I can't see why this wouldn't work in any Java project with Logback.
I tried using slf4j-test, but found, when running the test in eclipse, that I had several implementations of slf4j on the classpath, blocking slf4j-test. Their documentation (https://www.slf4j.org/codes.html#multiple_bindings) suggests some maven config that would sort out the test classpath when running tests via maven, but this didn't help me when running tests via eclipse.
In the end I implemented my own LogAppender (backed by a StringBuffer), attached that to my slf4j logger (provided by logback), and was able to write my test.
#Before
public void setUp() {
...
ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) (LoggerFactory
.getLogger(AdviserDashboardExceptionHandler.class));
appender = new StringBufferAppender();
logger.addAppender(appender);
}
#After
public void clearLogger() {
appender.clear();
}
#Test
public void loggingTest() {
...
assertThat(appender.getOutput(), containsString("some expected text"));
}
I needed to get this to work without using ch.qos.logback because that was conflicting with the usage of log4j2 in my main classes.
Hat tip to https://codingcraftsman.wordpress.com/2015/04/28/log4j2-mocking-with-mockito-and-junit/ for showing how to do it.
The thing that made a difference for me was casting the logger in the mocks to the logger from org.apache.logging.log4j.core.Logger, I was able to add an appender to it and it didn't break the rest of my code.
Pasting his code here for answer completeness.
import java.io.IOException;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
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 static org.junit.Assert.*;
import static org.mockito.Mockito.*;
#RunWith(MockitoJUnitRunner.class)
public class ExceptionLoggerTest {
#Mock
private Appender mockAppender;
private List<LogEvent> capturedEvents = new ArrayList<>();
private Logger logger;
#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);
// when append is called, convert the event to
// immutable and add it to the event list
doAnswer(answerVoid((LogEvent event) ->
capturedEvents.add(event.toImmutable()))
.when(mockAppender).append(any());
logger = (Logger)LogManager.getLogger(ExceptionLogger.class);
logger.addAppender(mockAppender);
logger.setLevel(Level.INFO);
}
#After /** as before */
#Test
public void loggingIsCaptured() {
logger.error("What an error");
verifyErrorMessages("What an error");
}
// inspecting messages involves just using the list of captured events
private void verifyErrorMessages(String ... messages) {
assertThat(capturedEvents.size(), is(messages.length));
int i=0;
for(LogEvent loggingEvent:capturedEvents) {
assertEquals(messages[i++],
loggingEvent.getMessage().getFormattedMessage());
}
}
}