sobota 21. dubna 2012

Common.Logging and compatibility with other libraries

It has been the second time since I have run into the issue of configuring correctly Common.Logging on my project. So what is the problem? Let's start with the basics:

Common.Logging should be a generic interface for logging which can be used by other frameworks and libraries to perform logging. The final user (you or me) uses several frameworks in his final application and if all of these frameworks will use different logging framework it will turn into configuration nightmare.So our favorite frameworks such as Spring.NET, Quartz.NET are using Common.Logging. This interface in turn uses a concrete logging framework to perform the logging (the act of writing the log lines to somewhere).

Typical scenario can be for instance Common.Logging and Log4Net combination. In our application configuration file (web.config or app.config) we have to configure Common.Logging to use the Log4Net and than we can continue with the Log4Net configuration specifying what should be logged.

<common>
<logging>
  <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net">
 <arg key="configType" value="INLINE" />
  </factoryAdapter>
</logging>
</common>

<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  <layout type="log4net.Layout.PatternLayout">
 <conversionPattern value="%date %-5level %logger - %message%newline"/>
  </layout>
</appender>
</log4net>

My general problem is that Common.Loggin.Log4Net facade is looking for a concrete version of the Log4Net library. Concretely the version: 'log4net (= 1.2.10)'. That is not a problem if you are not using some other framework which depends on higher version of Log4Net.
In my case the le_log4net library (the logentries library) is using log4net 2.0. So if you are using NuGet, you might obtain the following exception while adding the references:


image


The similar thing might happen if you just decide to use the latest Log4Net by default. Then you might get an exception when initializing Spring.NET context or starting the Quartz.NET scheduler:


Could not load file or assembly 'log4net, Version=1.2.0.30714, Culture=neutral, PublicKeyToken=b32731d11ce58905' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)


Solution 1: Ignore NuGet, define Runtime Binding


One way to get around this is to define runtime assembly binding. But this solution forces you to add the reference to log4net manually. NuGet controls the version and wont let you at references on the fly the way that you would. So to get over add the latest Common.logging.Log4net façade and Log4Net version 2 (which you need for some reason). Than you have to define the assembly  binding in the configuration file.

<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
 <assemblyIdentity name="Common.Logging" publicKeyToken="af08829b84f0328e"/>
 <bindingRedirect oldVersion="1.2.0.0" newVersion="2.0.0.0"/>
  </dependentAssembly>
</assemblyBinding>
</runtime>

Solution 2: Just use the older version of Log4Net (1.2.10)


If you do not have libraries that are dependent on Log4Net version 2.0.0 than just remember to always use log4net 1.2.10. This is the version which Common.Logging.Log4Net is looking for. Or just let NuGet manage it for you. You can add Common.Logging.Log4Net via NuGet and it will automatically load the correct version of Log4Net.


Solution 3: Try other logging library for instance NLog


This actually is not a real solution. I have experienced similar issues while using NLog, concretely try to use the latest NLog library with the Common.Logging.Nlog façade and you will obtain something similar to:


{"Could not load file or assembly 'NLog, Version=1.0.0.505, Culture=neutral, PublicKeyToken=5120e14c03d0593c' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)":"NLog, Version=1.0.0.505, Culture=neutral, PublicKeyToken=5120e14c03d0593c"}


The solution here is similar, you will have to define Runtime Binding:

<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
 <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
 <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
  </dependentAssembly>
</assemblyBinding>
</runtime>

What was interesting here, is that NuGet actually took care of this for me. I have just added the Common.Logging.NLog façade and I guess NuGet spotted that I have already NLog 2 and that this Runtime Binding is necessary. If you look at the documentation of bindingRedirect you will see, that we have the right to specify the range of versions in the oldVersion attribute. Here all the version will be bound to the 2.0.0.0 version.


Summary


Anyway NLog and Log4Net are both cool logging frameworks, just use the one you prefer. As I have showed  above it is possible to use them together with Common.Logging it just takes a few more lines to configure it correctly.

úterý 3. dubna 2012

Mock Java Calendar - JMockit vs Mockito

To get the current time or day in Java, one should be using the Calendar class in the following way:

Calendar c = Calendar.getInstance();
int day = c.get(Calendar.DAY_OF_WEEK);
Now I imagine this code can be hidden somewhere inside a business method and the behaviour of that method would be dependent on the current day. Typical example can be the method which returns the schedule of the cinema on the current day.
public class ScheduleService{
  public Schedule getTodaySchedule(){
    Calendar c = Calendar.getInstance();
    int day = c.get(Calendar.DAY_OF_WEEK);

    //get it from DB or wherever you want
    Schedule s = lookupAccordingToDay(day);
  }
}

In order to test this method you have to mock the Calendar. You will have to verify, that for monday the service will return the schedule for monday. However since the test will be automatically called every day, not only monday, you will obtain whole different schedules and the assert will fail. There are several solutions to this. I have 3 in my mind.

Solution 1: create a separate service

First solution has nothing to do with mocking. The way to go here is to isolate the Calendar into a separate service (let's call it CurrentDayService). Than you can manually create a mock for this service. You will also have to change the body of your ScheduleService to use this CurrentDayService.
public interface ICurrentDayService {
   int getCurrentDay();
}

public class CurrentDayService {
   public int getCurrentDay(){
      Calendar c = Calendar.getInstance();
      return c.get(Calendar.DAY_OF_WEEK);
   }
}

public class CurrentDayServiceMock {
   private int dayToReturn;
   public CurrentDayServiceMock(int dayToReturn){
     this.dayToReturn = dayToReturn;
   }
   public int getCurrentDay(){
      return dayToReturn;
   }
}

public class ScheduleService {
  //@Autowire or inject this service
  private CurrentDayService dayService;
  
  public Schedule getTodaySchedule(){
    int day = dayService.getCurrentDay();
    //get it from DB or wherever you want
    Schedule s = lookupAccordingToDay(day);
  }
}

Now in the unit test your schedule service, can use the mock instead of the real implementation. If you are using Dependency Injection than you can define a different context for unit tests. If not, you will have to do it manually.

Solution 2: use Mockito

Mockito allows you to mock the real Calendar class. That means that you no longer need to wrap the Calendar by some CurrentDayService class just to be able to mock the behavior. However you will still have to add a mechanism to pass the mocked Calendar to your service. That is not that complicated. Have a look at the following definition o the ScheduleService and the unit test which comes with it.
public class ScheduleService{
  private Calendar calendar;
  public ScheduleService(){
    calendar = Calendar.getInstance();
  }
  public Schedule getTodaySchedule(){
    int day = calendar.get(Calendar.DAY_OF_WEEK);
    Schedule s = lookupAccordingToDay(day);
  }

  public setCalendar(Calendar c){
    calendar = c;
  }
}

@Test
public void testGetTodaySchedule() {
 Calendar c = Mockito.mock(Calendar.class);
 Mockito.when(c.get(Calendar.DAY_OF_WEEK)).thenReturn(2);

 ScheduleService sService = new SomeStrangeService();
 //there has to be a way to set the current calendar
 sService.setCalendar(c);
 Schedule schedule = sService.getTodaySchedule();
 //Assert your schedule values
}

To sum it up: if the setCalendar method is not called, than the Calendar is instantiated in the constructor. So in production, it will return the current day. In your unit test, you can easily mock it, to specify different behavior. Tha drawback: if someone accidentaly calls the setCalendar method in the production, you will get into truble.

Solution 3: use JMockit, mock all the calendars in you JVM

JMockit is strong framework which as some other mocking frameworks is using the Java Instrumentation API. The code that you want to execute in your mocks is injected as bytecode at runtime. This enables JMockit to, for instance mock all the instances of Calendar class in your JVM. Here is how you can achieve this:
@MockClass(realClass = Calendar.class)
public static class CallendarMock {

 private int hour;
 private int day;
 private int minute;

 public CallendarMock(int day, int hour, int minute) {
  this.hour = hour;
  this.day = day;
  this.minute = minute;
 }

 @Mock
 public int get(int id) {
  if (id == Calendar.HOUR_OF_DAY) {
   return hour;
  }
  if (id == Calendar.DAY_OF_WEEK) {
   return day;
  }

  if (id == Calendar.MINUTE) {
   return minute;
  }
  return -1;
 }
}

The previous code snippet is the infrastructure which I can use to mock the Calendar's get method. A utility class CalendarMock has to be created, which specifies the methods which are mocked. The realClass attribute in the MockClass annotation specifies which class is mocked by the defined class. So now the unit test is simplified. There is not need to specify the Calendar which should be used by the ScheduleService.
@Test
public void testGetTodaySchedule() {
 Mockit.setUpMocks(new CallendarMock(Calendar.MONDAY, 12, 20));
 ScheduleService sService = new SomeStrangeService();
 Schedule schedule = sService.getTodaySchedule();
 //Assert your schedule values
}
@After
public void destroyMock() {
    Mockit.tearDownMocks();
}

At the end, you have to remember to switch-off the mocking of the Calendar. If not the Calendar will be mocked in all the tests executed after this one. Hence the call to the tearDownMocks() method.

Summary

With Mockito you can mock the real Calendar. However you have to pass the instance of the mocked callendar to the class, which actually uses it. With JMockit you are able to tell to the JVM: "from now all my mocks behave like this...". For me this simplifies the situation, while I am not forced to create a setter for a Calendar to be passed to my service class. But it would take much more time and effort to compare the two frameworks. It might be that Mockito handles some situations better than JMockit.