How can I mock a class which uses a SLF4J Log4J Logger? - java

I try to Mock a Class which instantiates a DatabaseConnection via Hibernate over an other Class, but I get errors of the SLF4J Error Factory in the Class, but I want the Logger to run in this Test to. How could I fix this to Mock the class and then instantiate the List and Logger successful?
I tried to Mock the Class (Ingredientadminsitration) and then this:
PowerMockito.mockStatic(LoggerFactory.class);
when(LoggerFactory.getLogger(any(Class.class))).
thenReturn(loggerMock);
Ingredientadministration.java
#Slf4j
public class Ingredientsadministration {
private ObservableList<Ingredient> zutaten;
private SQLConnection sqlConnection;
private static Ingredientsadministration ingredientsadministration;
private Logger logger;
private Ingredientsadministration() {
logger = LoggerFactory.getLogger(this.getClass());
connectToDB();
zutaten = FXCollections.observableArrayList(sqlConnection.getZutaten());
}
public static Ingredientsadministration getInstance() {
if (ingredientsadministration == null) {
ingredientsadministration = new Ingredientsadministration();
}
return ingredientsadministration;
}
MySQLHibernate.java
#Slf4j
public class MySQLConnectHibernate implements SQLConnection {
private static SessionFactory sessionFactory;
private Session session;
private Logger logger;
private static MySQLConnectHibernate entity;
private MySQLConnectHibernate() throws ServiceException {
logger = LoggerFactory.getLogger(this.getClass());
setup();
}
public static MySQLConnectHibernate getInstance() {
if (entity == null) {
entity = new MySQLConnectHibernate();
}
return entity;
}
private void setup() throws ServiceException {
if (sessionFactory == null) {
sessionFactory = new Configuration().configure().buildSessionFactory();
logger.debug("Create new SessionFactory={}.", sessionFactory);
}
}

I suggest you want to test if the specific message is logged? First of all if you use #sl4j you don't have to instantiate a logger by yourself. Lombok will do this for you (log.debug(..)) should be used then. You can receive the logger in your test via LoggerFactory.getLogger(SomeClass.class); You can attache a mocked appender and check if the wanted log entry was written to your mocked appender. Here's a simple example: https://dzone.com/articles/unit-testing-asserting-line

Related

how do i mock method, calling one method from another method?

I'm New in Mocking.
I've a service I'm trying to call is let say name A, I need to test someMethod.
#Service
public class A {
private Logger logger = LoggerFactory.getLogger(getClass());
private final CoreXReader coreXReader;
#Autowired
B b;
#Autowired
C c;
#Async
public void someMethod(Config config) throws Exception {
pushConfig(config);
}
private void pushConfig(Config config) throws Exception {
String url = config.getBaseurl() + config.getId();
ABCRestClient restClient = new ABCRestClient(url);
String jobJson = restClient.callRestMethod(HttpMethod.GET, "");
}
}
sample of ABCRestClient
public class ABCRestClient {
private Logger logger = LoggerFactory.getLogger(getClass());
private String url;
public ABCRestClient(String url) {
this.url = url;
}
public String callRestMethod(HttpMethod method, String payload) throws Exception {
someresponse="example response";
return someresponse;
}
}
I'm trying to test by creating mockSpy but it still Calling its 'callRestMethod'
#RunWith(SpringRunner.class)
#SpringBootTest // (webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test {
#Autowired
private A a;
private Logger logger = LoggerFactory.getLogger(getClass());
#Before
public void prepareMockDataForService() throws Exception {
ABCRestClient apiClient = new ABCRestClient(config.getBaseurl() + config.getId() );
ABCRestClient apiClientSpy=Mockito.spy(apiClient);
doReturn(getCallResponse()).when(apiClientSpy).callRestMethod(HttpMethod.GET, "");
}
#Test
public void TestPushConfig() throws Exception {
a.someMethod(StubDataGenerator.getConfig());
}
private String getCallResponse() {
return "{"msg":"sample response"}";
}
}
i'm not sure what I'm doing wrong here why its calling the actual callRestMethod as i already create a spy .
I tried using this too Mockito.doReturn(getCallResponse()).when(apiClientSpy.callRestMethod(HttpMethod.GET, ""))
Also, is there any difference in these two statement if I use it Mockito.doReturn() or directly doReturn()? In my case both seems behaving same.
Before I tried with this as well when().thenReturn(); but I read somewhere that use when().thenReturn() when you actually want to make call. Please correct if my understanding is wrong.
You can try mock instead of spy:
#RunWith(SpringRunner.class)
#SpringBootTest // (webEnvironment=
SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test {
#Autowired
private A a;
private Logger logger = LoggerFactory.getLogger(getClass());
#Before
public void prepareMockDataForService() throws Exception {
ABCRestClient apiClientSpy=Mockito.mock(ABCRestClient.class);
doReturn(getCallResponse()).when(apiClientSpy).callRestMethod(HttpMethod.GET, "");
}
#Test
public void TestPushConfig() throws Exception {
a.someMethod(StubDataGenerator.getConfig());
}
private String getCallResponse() {
return "{"msg":"sample response"}";
}
}

How to mock generated object and its attribute in power mockito

I am creating an adapter web service that has configuration (codes below).
I am able to mock the 'app'
object but its attribute 'datasource' is null even I have mocked it. How can I mock the data source and its connection attribute?
MyAdapter.java
public class MyAdapter {
#Context
private ConfigurationAPI configApi;
#Context
private AdaptersAPI adaptersAPI;
public Connection getSQLConnection() throws SQLException {
JavaAdapter app = adaptersAPI.getJaxRsApplication(JavaAdapter.class);
return app.getDataSource().getConnection();
}
}
MyAdapterTest.java
#RunWith(PowerMockRunner.class);
public class MyAdapterTest {
#Mock
DataSource dataSource;
#Mock
private ConfigurationAPI configApi;
#Mock
private AdaptersAPI adaptersAPI;
#InjectMocks
MyJavaAdapter myAdapter;
private MyApp app = new MyApp();
#Test
public void getSQLConnectionTest() throws SQLException {
PowerMockito.when(adaptersAPI.getJaxRsApplication(JavaAdapter.class).thenReturn(app);
PowerMockito.when(app.getDataSource()).thenReturn(dataSource);
}
}
MyApp.java
public class MyApp extends MFPJAXRSApplication{
private DataSource dataSource = null;
#Override
protected void init() throws Exception {
InitialContext ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup("customPath");
}
#Override
protected void destroy() throws Exception {
}
#Override
protected String getPackageToScan() {
return getClass().getPackage().getName();
}
public DataSource getDataSource() {
return dataSource;
}
Try to reaplace this:
private MyApp app = new MyApp();
with this:
#InjectMocks
private MyApp app;
You cannot set the mock instances on the objects that are not mocked (You can set them by calling the setter methods though). Instead try to mock MyApp
#Mock
private MyApp app
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
Then you can use the below statement to mock the DataSource
PowerMockito.when(app.getDataSource()).thenReturn(dataSource);

slf4j add custom method

I have to add custom method method to my logging system. Let's say I have in several places line of code like:
private static Logger LOGGER = LoggerFactory.getLogger(MyJavaClass.class);
Logging is handling by slf4j. How can I extend my logger with adding new(!) method like public void error(SomeTypeObject obj) { /implementation/ }
The goal is to not change existing code. How can I force LoggerFactory to return my own Logger implementation extended with mentioned method?
I followed answers for this queston: stackoverflow.com/questions/2653855/implement-custom-logger-with-slf4j
So, I've made my LoggerFactory, StaticLoggerBinder and MyLoggerAdapter.
StaticLoggerBinder
public class StaticLoggerBinder implements LoggerFactoryBinder {
private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
public static final StaticLoggerBinder getSingleton() {
return SINGLETON;
}
public static String REQUESTED_API_VERSION = "1.6.99";
private static final String loggerFactoryClassStr = MyLoggerFactory.class.getName();
private final ILoggerFactory loggerFactory;
private StaticLoggerBinder() {
loggerFactory = new MyLoggerFactory();
}
public ILoggerFactory getLoggerFactory() {
return loggerFactory;
}
public String getLoggerFactoryClassStr() {
return loggerFactoryClassStr;
}
}
Logger Factory
public class MyLoggerFactory implements ILoggerFactory {
ConcurrentMap<String, Logger> loggerMap;
public MyLoggerFactory() {
loggerMap = new ConcurrentHashMap<String, Logger>();
}
public Logger getLogger(String name) {
Logger simpleLogger = loggerMap.get(name);
if (simpleLogger != null) {
return simpleLogger;
} else {
Logger newInstance = new MyLoggerAdapter(name);
Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
return oldInstance == null ? newInstance : oldInstance;
}
}
void reset() {
loggerMap.clear();
}
}
MyLogger
public class MyLoggerAdapter implements Logger {
//methods from Logger interface
//and my additional method
public void error(Exception ex) {
//do smthng;
}
}
Now, in MyJavaClass i have a field
private static Logger LOGGER = LoggerFactory.getLogger(MyJavaClass.class);
The problem is, when i try to LOGGER.error(myExceptionObject) the method is not visible. I am missing something. I would be very grateful for help.
In your MyJavaClass you should use your MyLoggerFactory:
private static MyLoggerAdapter LOGGER = MyLoggerFactory.getLogger();
instead of
private static Logger LOGGER = LoggerFactory.getLogger(MyJavaClass.class);
In this example logger can get so:
private static MyLoggerAdapter log = (MyLoggerAdapter) StaticLoggerBinder.getSingleton().getLoggerFactory.getLogger(MyController.class.getName());

Use Dropwizard configuration in a method that establishes a connection to a MongoDB database

I am coding Dropwizard micro-services that fetch data in a MongoDB database. The micro-services work fine but I'm struggling to use in my DAO the configuration coming from my Dropwizard configuration Java class. Currently I have
public class XDAO implements IXDAO {
protected DB db;
protected DBCollection collection;
/* singleton */
private static XDAO instance;
/* Get singleton */
public static synchronized XDAO getSingleton(){
if (instance == null){
instance = new XDAO();
}
return instance;
}
/* constructor */
public XDAO(){
initDatabase();
initDatabaseIndexes();
}
private void initDatabase(){
MongoClient client = null;
try {
client = new Mongo("10.126.80.192",27017);
db = client.getDB("terre");
//then some other code
}
catch (final MongoException e){
...
}
catch (UnknownHostException e){
...
}
}
}
I want to unhard-code the three arguments in these two lines :
client = new Mongo("10.126.80.192", 27017);
db = client.getDB("terre");
My MongoConfiguration Java class is :
public class MongoConfiguration extends Configuration {
#JsonProperty
#NotEmpty
public String host;
#JsonProperty
public int port = 27017;
#JsonProperty
#NotEmpty
public String db_name;
public String getMongohost() {
return host;
}
public void setMongohost(String host) {
this.host = host;
}
public int getMongoport() {
return port;
}
public void setMongoport(int port) {
this.port = port;
}
public String getDb_name() {
return db_name;
}
public void setDb_name(String db_name) {
this.db_name = db_name;
}
}
My Resource class that uses the DAO is :
#Path("/mongo")
#Produces(MediaType.APPLICATION_JSON)
public class MyResource {
private XDAO xDAO = XDAO.getSingleton();
private String mongohost;
private String db_name;
private int mongoport;
public MyResource(String db_name, String mongohost, int mongoport) {
this.db_name = db_name;
this.mongohost = mongohost;
this.mongoport = mongoport;
}
public MyResource() {
}
#GET
#Path("/findByUUID")
#Produces(value = MediaType.APPLICATION_JSON)
#Timed
public Entity findByUUID(#QueryParam("uuid") String uuid) {
return xDAO.findByUUid(uuid);
}
}
And in my application class there is
#Override
public void run(final MongoConfiguration configuration, final Environment environment) {
final MyResource resource = new MyResource(configuration.getDb_name(), configuration.getMongohost(), configuration.getMongoport());
environment.jersey().register(resource);
}
To solve my problem I tried many things. The last thing I tried was to add these four fields in my XDAO
private String mongohost;
private String db_name;
private int mongoport;
private static final MongoConfiguration configuration = new MongoConfiguration();
Coming with this piece of code in the constructor of the XDAO:
public XDAO(){
instance.mongohost = configuration.getMongohost();
instance.mongoport = configuration.getMongoport();
instance.db_name = configuration.getDb_name();
/* then like before */
initDatabase();
initDatabaseIndexes();
}
When I try this I have a null pointer exception when my initDatabase method is invoked : mongoHost and db_name are null
The problem is that you are creating a new configuration in your XDAO with private static final MongoConfiguration configuration = new MongoConfiguration(); instead of using the config from Dropwizard's run method.
When you do this, the fields host and db_name in the new configuration are null, which is why you are getting the NPE when instantiating XDAO
You need to pass the instance of MongoConfiguration that you get from Dropwizard in your application class to your XDAO, ideally when the singleton XDAO is created so it has non-null values for db_name and host
This code below part of the problem - you are creating the singleton without giving XDAO the MongoConfiguration configuration instance.
public class XDAO implements IXDAO {
//... snip
/* Get singleton */
public static synchronized XDAO getSingleton(){
if (instance == null){
instance = new XDAO(); // no configuration information is included!
}
return instance;
}
/* constructor */
public XDAO(){
initDatabase(); // this call needs db_name & host but you haven't set those yet!!
initDatabaseIndexes();
}
I recommend you modify your application class to create XDAO along the lines of this:
#Override
public void run(final MongoConfiguration configuration, final Environment environment) {
XDAO XDAOsingleton = new XDAO(configuration);
XDAO.setSingletonInstance(XDAOsingleton); // You need to create this static method.
final MyResource resource = new MyResource(configuration.getDb_name(), configuration.getMongohost(), configuration.getMongoport()); // MyResource depends on XDAO so must be created after XAO's singleton is set
environment.jersey().register(resource);
}
You may also need to take initDatabase() etc out of XDAO's constructor depending on if you keep public static synchronized XDAO getSingleton()
I also recommend you change the constructor of MyResource to public MyResource(XDAO xdao). The resource class doesn't appear to need the configuration information, and it is better to make the dependency on an XDAO explicit (you then also don't need to keep the XDAO singleton in a static field inside XDAO's class).
To get MongoDB integrated in a simple way to Dropwizard, please try and use MongoDB Managed Object. I will explain this in 3 simple steps:
Step 1: Create a simple MongoManged class:
import com.mongodb.Mongo;
import io.dropwizard.lifecycle.Managed;
public class MongoManaged implements Managed {
private Mongo mongo;
public MongoManaged(Mongo mongo) {
this.mongo = mongo;
}
#Override
public void start() throws Exception {
}
#Override
public void stop() throws Exception {
mongo.close();
}
}
Step 2: Mention MongoDB Host, Port, DB Name in a config yml file:
mongoHost : localhost
mongoPort : 27017
mongoDB : softwaredevelopercentral
Step 3: Bind everything together in the Application Class:
public class DropwizardMongoDBApplication extends Application<DropwizardMongoDBConfiguration> {
private static final Logger logger = LoggerFactory.getLogger(DropwizardMongoDBApplication.class);
public static void main(String[] args) throws Exception {
new DropwizardMongoDBApplication().run("server", args[0]);
}
#Override
public void initialize(Bootstrap<DropwizardMongoDBConfiguration> b) {
}
#Override
public void run(DropwizardMongoDBConfiguration config, Environment env)
throws Exception {
MongoClient mongoClient = new MongoClient(config.getMongoHost(), config.getMongoPort());
MongoManaged mongoManaged = new MongoManaged(mongoClient);
env.lifecycle().manage(mongoManaged);
MongoDatabase db = mongoClient.getDatabase(config.getMongoDB());
MongoCollection<Document> collection = db.getCollection(config.getCollectionName());
logger.info("Registering RESTful API resources");
env.jersey().register(new PingResource());
env.jersey().register(new EmployeeResource(collection, new MongoService()));
env.healthChecks().register("DropwizardMongoDBHealthCheck",
new DropwizardMongoDBHealthCheckResource(mongoClient));
}
}
I have used these steps and written a blog post and a sample working application code is available on GitHub. Please check: http://softwaredevelopercentral.blogspot.com/2017/09/dropwizard-mongodb-tutorial.html

Mocking dependencies and code coverage tool shows not executed code

i'm trying to test my bean as a component itself. So that the method i want to test gets executed correctly. Therefore i'm mocking it's dependencies with JMockit. I wrote two tests, one for validating the if condition to true, so the method ends immediately and returns null.The second one for executing the code below this condition also resulting in returning null. But my code coverage tool (JaCoCo) just shows that the if condition is executed and not the code below.
Session is a field in the super class ÀbstractBean. The method isLoggedIn() invokes session.isLoggedIn() and is defined in AbstractBean.
TrendBean
#Named(value = "trendBean")
#ViewScoped
#SuppressWarnings("deprecation")
public class TrendBean extends AbstractBean implements Serializable {
private static final long serialVersionUID = -310401000218411384L;
private static final Logger logger = Logger.getLogger(TrendBean.class);
private ChartPoint point;
private List<ChartPoint> points;
#Inject
private ITrendManager manager;
public String addChartPoint() {
if (!isLoggedIn()) {
return null; //only this block is executed
}
assertNotNull(point);
final User user = getSession().getUser();
manager.addPointToUser(user, point);
FacesContext.getCurrentInstance().addMessage(
null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
getTranslation("pointAdded"), ""));
init();
return null;
}
}
TrendBeanTest
public class TrendBeanTest {
#Tested
TrendBean trendBean;
#Injectable
LoginBean loginBean;
#Injectable
Session session;
#Injectable
ITrendManager manager;
#Injectable
IUserManager userManager;
#Test
public void testAddChartPoint() {
new NonStrictExpectations() {
{
session.isLoggedIn();
result = true;
session.getUser();
result = (User) any;
manager.addPointToUser((User) any, (ChartPoint) any);
};
};
Deencapsulation.setField(trendBean, "point", new ChartPoint());
assertEquals(null, trendBean.addChartPoint());
}
#Test
public void testAddChartPointNotLoggedIn() {
new Expectations() {
{
manager.addPointToUser((User) any, (ChartPoint) any);
times = 0;
};
};
Session s = new Session();
s.setUser(null);
Deencapsulation.setField(trendBean, "session", s);
assertEquals(null, trendBean.addChartPoint());
}
}
AbstractBean
public abstract class AbstractBean {
private static final Logger logger = Logger.getLogger(AbstractBean.class);
#Inject
private Session session;
public boolean isLoggedIn() {
return session.isLoggedIn();
}
}
For anyone having familiar problems the statement
session.isLoggedIn();
result = true;
in Expectations Block was the answer. Although i'm facing new problems, i'm going to ask a new question.

Categories