Comment puis-je tester avec junit qu’un avertissement a été enregistré avec log4j?

Je teste une méthode qui enregistre les avertissements en cas de problème et renvoie la valeur null.
quelque chose comme:

private static final Logger log = Logger.getLogger(Clazz.class.getName()); .... if (file == null || !file.exists()) { // if File not found log.warn("File not found: "+file.toSsortingng()); } else if (!file.canWrite()) { // if file is read only log.warn("File is read-only: "+file.toSsortingng()); } else { // all checks passed, can return an working file. return file; } return null; 

Je voudrais tester avec junit qu’un avertissement a été émis, en plus de renvoyer null, dans tous les cas (par exemple, fichier introuvable, fichier en lecture seule).
des idées?
merci asaf 🙂


METTRE À JOUR

Mon implémentation de la réponse d’Aaron (plus la remarque de Peter):

 public class UnitTest { ... @BeforeClass public static void setUpOnce() { appenders = new Vector(2); // 1. just a printout appender: appenders.add(new ConsoleAppender(new PatternLayout("%d [%t] %-5p %c - %m%n"))); // 2. the appender to test against: writer = new SsortingngWriter(); appenders.add(new WriterAppender(new PatternLayout("%p, %m%n"),writer)); } @Before public void setUp() { // Unit Under Test: unit = new TestUnit(); // setting test appenders: for (Appender appender : appenders) { TestUnit.log.addAppender(appender); } // saving additivity and turning it off: additivity = TestUnit.log.getAdditivity(); TestUnit.log.setAdditivity(false); } @After public void tearDown() { unit = null; for (Appender appender : appenders) { TestUnit.log.removeAppender(appender); } TestUnit.log.setAdditivity(additivity); } @Test public void testGetFile() { // start fresh: File file; writer.getBuffer().setLength(0); // 1. test null file: System.out.println(" 1. test null file."); file = unit.getFile(null); assertNull(file); assertTrue(writer.toSsortingng(), writer.toSsortingng().startsWith("WARN, File not found")); writer.getBuffer().setLength(0); // 2. test empty file: System.out.println(" 2. test empty file."); file = unit.getFile(""); assertNull(file); assertTrue(writer.toSsortingng(), writer.toSsortingng().startsWith("WARN, File not found")); writer.getBuffer().setLength(0); } 

Merci les gars,

Dans la configuration du test unitaire:

  1. Obtenez le même enregistreur
  2. Le rendre non additif
  3. Ajoutez un appender qui mémorise les messages dans une liste:

     public class TestAppender extends AppenderSkeleton { public List messages = new ArrayList(); public void doAppend(LoggingEvent event) { messages.add( event.getMessage().toSsortingng() ); } } 
  4. Ajouter l’appender à l’enregistreur

Maintenant, vous pouvez appeler votre code. Après le test, vous trouverez tous les messages de journal dans la liste. Ajoutez le niveau de journalisation si vous le souhaitez ( messages.add( event.getLevel() + " " + event.getMessage() ); ).

Dans tearDown() , supprimez à nouveau l’appender et activez l’additivité.

En utilisant Mockito, vous pouvez tester la journalisation survenue lors de votre test avec un code de plaque de chaudière minimal, un exemple simple est:

 @RunWith(MockitoJUnitRunner.class) public class TestLogging { @Mock AppenderSkeleton appender; @Captor ArgumentCaptor logCaptor; @Test public void test() { Logger.getRootLogger().addAppender(appender); ...... verify(appender).doAppend(logCaptor.capture()); assertEquals("Warning message should have been logged", "Caution!", logCaptor.getValue().getRenderedMessage()); } } 

Une alternative à la solution d’Aaron serait de configurer un WriterAppender avec un SsortingngWriter attaché. À la fin du test, vous pouvez vérifier le contenu de la chaîne de sortie du journal.

C’est un peu plus facile à implémenter (pas besoin de code personnalisé), cependant, il est moins flexible en ce qui concerne la vérification des résultats, car vous obtenez uniquement la sortie en texte brut. Dans certains cas, il peut être plus difficile de vérifier la sortie qu’avec la solution d’Aaron.

Les exemples de cet article ont été très utiles, mais je les ai trouvés peu compliqués.
J’ajoute donc une version simplifiée pour ce qui précède avec quelques modifications mineures.

  • J’ajoute mon appender à l’enregistreur racine.

De cette façon, et en supposant que additive est vrai par défaut, je n’aurai pas à me soucier de perdre mes événements en raison de la hiérarchie des enregistreurs. Assurez-vous que cela correspond à votre configuration de fichier log4j.properties.

  • Je supplante append et non faisAppend.

Ajouter dans AppenderSkeleton traite du filtrage de niveau, donc je ne veux pas manquer ça.
doAppend appellera append si le niveau est correct.

 public class TestLogger { @Test public void test() { TestAppender testAppender = new TestAppender(); Logger.getRootLogger().addAppender(testAppender); ClassUnderTest.logMessage(); LoggingEvent loggingEvent = testAppender.events.get(0); //asset equals 1 because log level is info, change it to debug and //the test will fail assertTrue("Unexpected empty log",testAppender.events.size()==1); assertEquals("Unexpected log level",Level.INFO,loggingEvent.getLevel()); assertEquals("Unexpected log message" ,loggingEvent.getMessage().toSsortingng() ,"Hello Test"); } public static class TestAppender extends AppenderSkeleton{ public List events = new ArrayList(); public void close() {} public boolean requiresLayout() {return false;} @Override protected void append(LoggingEvent event) { events.add(event); } } public static class ClassUnderTest { private static final Logger LOGGER = Logger.getLogger(ClassUnderTest.class); public static void logMessage(){ LOGGER.info("Hello Test"); LOGGER.debug("Hello Test"); } } } 

log4j.properties

 log4j.rootCategory=INFO, CONSOLE log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d %p [%c] - %m%n # un-comment this will fail the test #log4j.logger.com.haim.logging=DEBUG 

Au lieu d’appeler directement log4j, utilisez une méthode protégée dans votre classe.

Quelque chose comme:

 protected void log(Ssortingng message, Level level) { //delegates to log4j } 

Créez ensuite une sous-classe de la classe testée qui remplit cette méthode, afin de pouvoir vérifier qu’elle est appelée comme prévu.

 class MyTest extends  { boolean somethingLogged = false; protected void log(Ssortingng message, Level level) { somethingLogged = true; } } 

puis affirmer sur la base de quelque choseLogged. Vous pouvez append une logique conditionnelle dans la méthode de substitution t test en fonction du message / niveau attendu.

Vous pouvez aller plus loin et enregistrer toutes les invocations, puis rechercher dans les messages enregistrés, ou vérifier qu’ils ont été enregistrés dans le bon ordre, etc.

J’adapte la réponse de Haim à quelque chose de plus RAII:

 public static class TestAppender extends AppenderSkeleton { @Override protected void append(LoggingEvent event) { messages.add(event.getRenderedMessage()); } @Override public void close() { } @Override public boolean requiresLayout() { return false; } protected final List messages = new ArrayList<>(); } static class LogGuard implements AutoCloseable { protected final TestAppender appender; LogGuard(Level level) { appender = new TestAppender(); appender.setThreshold(level); Logger.getRootLogger().addAppender(appender); } @Override public void close() throws Exception { Logger.getRootLogger().removeAppender(appender); } } 

Et puis, l’utilisation est simplement:

 try (LogGuard log = new LogGuard(Level.WARN)) { // if you want WARN or higher // do what causes the logging Assert.assertTrue(log.appender.messages.stream().anyMatch(m -> m.equals("expected")); }