J'ai également eu besoin de cela plusieurs fois. J'ai rassemblé un petit échantillon ci-dessous, que vous souhaitez adapter à vos besoins. Fondamentalement, vous créez le vôtre Appender
et l'ajoutez à l'enregistreur que vous souhaitez. Si vous souhaitez tout collecter, l'enregistreur racine est un bon point de départ, mais vous pouvez en utiliser un plus spécifique si vous le souhaitez. N'oubliez pas de supprimer l'Appender lorsque vous avez terminé, sinon vous pourriez créer une fuite de mémoire. Ci-dessous, je l'ai fait dans le cadre du test, mais setUp
ou @Before
et tearDown
ou @After
pourraient être de meilleurs endroits, selon vos besoins.
De plus, l'implémentation ci-dessous recueille tout dans une List
mémoire. Si vous vous connectez beaucoup, vous pouvez envisager d'ajouter un filtre pour supprimer les entrées ennuyeuses ou d'écrire le journal dans un fichier temporaire sur le disque (indice: LoggingEvent
est Serializable
, vous devriez donc être en mesure de sérialiser simplement les objets d'événement, si votre message de journal est.)
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class MyTest {
@Test
public void test() {
final TestAppender appender = new TestAppender();
final Logger logger = Logger.getRootLogger();
logger.addAppender(appender);
try {
Logger.getLogger(MyTest.class).info("Test");
}
finally {
logger.removeAppender(appender);
}
final List<LoggingEvent> log = appender.getLog();
final LoggingEvent firstLogEntry = log.get(0);
assertThat(firstLogEntry.getLevel(), is(Level.INFO));
assertThat((String) firstLogEntry.getMessage(), is("Test"));
assertThat(firstLogEntry.getLoggerName(), is("MyTest"));
}
}
class TestAppender extends AppenderSkeleton {
private final List<LoggingEvent> log = new ArrayList<LoggingEvent>();
@Override
public boolean requiresLayout() {
return false;
}
@Override
protected void append(final LoggingEvent loggingEvent) {
log.add(loggingEvent);
}
@Override
public void close() {
}
public List<LoggingEvent> getLog() {
return new ArrayList<LoggingEvent>(log);
}
}
Ronald Blaschke
la source
logger.getAllAppenders()
, puis de passer à travers et d'appelerappender.setThreshold(Level.OFF)
chacun (et de les réinitialiser lorsque vous avez terminé!). Cela garantit que les "mauvais" messages que vous essayez de générer n'apparaissent pas dans les journaux de test et ne paniquent pas le prochain développeur.ListAppender<ILoggingEvent>
au lieu de créer votre propre appender personnalisé.Logger
versorg.apache.logging.log4j.core.Logger
(la classe d'implémentation de l'interface), vous aurez àsetAppender()/removeAppender()
nouveau accès à .Voici une solution Logback simple et efficace.
Il ne nécessite pas d'ajouter / créer de nouvelle classe.
Il repose sur
ListAppender
: un appender de retour en arrière de la boîte blanche où les entrées de journal sont ajoutées dans unpublic List
champ que nous pourrions ainsi utiliser pour faire nos assertions.Voici un exemple simple.
Classe Foo:
Classe FooTest:
Les assertions JUnit ne semblent pas très adaptées pour affirmer certaines propriétés spécifiques des éléments de la liste.
Les bibliothèques d'association / assertion comme AssertJ ou Hamcrest semblent mieux pour cela:
Avec AssertJ ce serait:
la source
mock
la classe en cours de test. Vous devez l'instancier avec l'new
opérateurMerci beaucoup pour ces réponses (étonnamment) rapides et utiles; ils m'ont mis sur la bonne voie pour ma solution.
La base de code où je veux l'utiliser, utilise java.util.logging comme mécanisme d'enregistrement, et je ne me sens pas suffisamment à l'aise dans ces codes pour changer complètement cela en log4j ou en interfaces / façades d'enregistreur. Mais sur la base de ces suggestions, j'ai «piraté» une extension julhandler et cela fonctionne comme un régal.
Un bref résumé suit. Étendre
java.util.logging.Handler
:De toute évidence, vous pouvez stocker autant que vous le souhaitez / voulez / besoin de la
LogRecord
, ou les pousser tous dans une pile jusqu'à ce que vous obteniez un débordement.Dans la préparation du junit-test, vous créez un
java.util.logging.Logger
et ajoutez-en un nouveauLogHandler
:L'appel à
setUseParentHandlers()
est de faire taire les gestionnaires normaux afin que (pour cette exécution de test de junit) aucune journalisation inutile ne se produise. Faites tout ce dont votre code sous test a besoin pour utiliser cet enregistreur, exécutez le test et assertEquality:(Bien sûr, vous déplaceriez une grande partie de ce travail dans une
@Before
méthode et apporteriez d'autres améliorations, mais cela encombrerait cette présentation.)la source
Une autre option consiste à se moquer de Appender et à vérifier si le message a été enregistré sur cet appender. Exemple pour Log4j 1.2.x et mockito:
la source
En fait, vous testez un effet secondaire d'une classe dépendante. Pour les tests unitaires, il vous suffit de vérifier que
a été appelé avec le paramètre correct. Par conséquent, utilisez un cadre de simulation pour émuler l'enregistreur et cela vous permettra de tester le comportement de votre propre classe.
la source
Le mocking est une option ici, bien que ce soit difficile, car les enregistreurs sont généralement des statiques privés finaux - donc la configuration d'un mock logger ne serait pas un jeu d'enfant, ou nécessiterait une modification de la classe testée.
Vous pouvez créer un Appender personnalisé (ou son nom) et l'enregistrer - soit via un fichier de configuration de test uniquement, soit à l'exécution (d'une certaine manière, en fonction du cadre de journalisation). Et puis vous pouvez obtenir cet appender (soit statiquement, s'il est déclaré dans le fichier de configuration, soit par sa référence actuelle, si vous le branchez à l'exécution) et vérifier son contenu.
la source
Inspiré par la solution de @ RonaldBlaschke, j'ai trouvé ceci:
... ce qui vous permet de faire:
Vous pourriez probablement lui faire utiliser hamcrest de manière plus intelligente, mais je l'ai laissé là.
la source
Pour log4j2, la solution est légèrement différente car AppenderSkeleton n'est plus disponible. En outre, l'utilisation de Mockito ou d'une bibliothèque similaire pour créer un Appender avec un ArgumentCaptor ne fonctionnera pas si vous attendez plusieurs messages de journalisation car le MutableLogEvent est réutilisé sur plusieurs messages de journal. La meilleure solution que j'ai trouvée pour log4j2 est:
la source
Comme mentionné dans les autres, vous pouvez utiliser un cadre de simulation. Pour que cela fonctionne, vous devez exposer l'enregistreur dans votre classe (bien que je préfère de préférence le rendre privé au lieu de créer un setter public).
L'autre solution consiste à créer un faux enregistreur à la main. Vous devez écrire le faux logger (plus de code de montage) mais dans ce cas, je préférerais la lisibilité améliorée des tests par rapport au code enregistré du framework de simulation.
Je ferais quelque chose comme ça:
la source
Sensationnel. Je ne sais pas pourquoi cela a été si difficile. J'ai trouvé que je ne pouvais pas utiliser l'un des exemples de code ci-dessus parce que j'utilisais log4j2 sur slf4j. Voici ma solution:
la source
Voici ce que j'ai fait pour la déconnexion.
J'ai créé une classe TestAppender:
Ensuite, dans le parent de ma classe de test unitaire testng, j'ai créé une méthode:
J'ai un fichier logback-test.xml défini dans src / test / resources et j'ai ajouté un appender de test:
et a ajouté cet appender à l'appendice racine:
Maintenant, dans mes classes de test qui s'étendent de ma classe de test parent, je peux obtenir l'appender et obtenir le dernier message enregistré et vérifier le message, le niveau, le jetable.
la source
Pour Junit 5 (Jupiter), OutputCaptureExtension de Spring est très utile. Il est disponible depuis Spring Boot 2.2 et est disponible dans l' artefact spring-boot-test .
Exemple (tiré de javadoc):
la source
getOut()
ougetErr()
.Quant à moi, vous pouvez simplifier votre test en utilisant
JUnit
avecMockito
. Je lui propose la solution suivante:C'est pourquoi nous avons une belle flexibilité pour les tests avec différentes quantités de messages
la source
when(appender.isStarted()).thenReturn(true); when(appender.getName()).thenReturn("Test Appender");
convertir le journal en "org.apache.logging.log4j.core.Logger", d'ajouter et de modifier LoggingEvent -> LogEventla source
L'API pour Log4J2 est légèrement différente. Vous pouvez également utiliser son appender asynchrone. J'ai créé un appender verrouillé pour cela:
Utilisez-le comme ceci:
la source
Notez que dans Log4J 2.x, l'interface publique
org.apache.logging.log4j.Logger
n'inclut pas les méthodessetAppender()
etremoveAppender()
.Mais si vous ne faites rien de trop sophistiqué, vous devriez pouvoir le convertir en classe d'implémentation
org.apache.logging.log4j.core.Logger
, qui expose ces méthodes.Voici un exemple avec Mockito et AssertJ :
la source
Une autre idée mérite d'être mentionnée, bien qu'il s'agisse d'un sujet plus ancien, la création d'un producteur CDI pour injecter votre enregistreur afin que la moquerie devienne facile. (Et cela donne également l'avantage de ne plus avoir à déclarer la "totalité de l'instruction logger", mais c'est hors sujet)
Exemple:
Création de l'enregistreur à injecter:
Le qualificatif:
Utilisation de l'enregistreur dans votre code de production:
Test de l'enregistreur dans votre code de test (donnant un exemple easyMock):
la source
En utilisant Jmockit (1.21), j'ai pu écrire ce test simple. Le test garantit qu'un message ERREUR spécifique est appelé une seule fois.
la source
Se moquer de l'Appender peut aider à capturer les lignes de journal. Trouvez un exemple sur: http://clearqa.blogspot.co.uk/2016/12/test-log-lines.html
la source
Utilisez le code ci-dessous. J'utilise le même code pour mon test d'intégration de printemps où j'utilise le journal de connexion pour la journalisation. Utilisez la méthode assertJobIsScheduled pour affirmer le texte imprimé dans le journal.
la source
si vous utilisez
java.util.logging.Logger
cet article peut être très utile, il crée un nouveau gestionnaire et fait des assertions sur le journal Sortie: http://octodecillion.com/blog/jmockit-test-logging/la source
Il y a deux choses que vous essayez peut-être de tester.
Ces deux choses sont en fait des choses différentes et pourraient donc être testées séparément. Cependant, tester le second (le texte des messages) est tellement problématique, je déconseille de le faire du tout. Un test d'un texte de message consistera finalement à vérifier qu'une chaîne de texte (le texte de message attendu) est identique à la chaîne de texte utilisée dans votre code de consignation ou peut en être dérivée de manière triviale.
Notez que le fait d'avoir votre code de programme (implémentant une logique métier, peut-être) appelant directement l'interface de journalisation de texte est une mauvaise conception (mais malheureusement très commode). Le code responsable de la logique métier détermine également une stratégie de journalisation et le texte des messages de journal. Il mélange la logique métier avec le code de l'interface utilisateur (oui, les messages de journalisation font partie de l'interface utilisateur de votre programme). Ces choses devraient être séparées.
Je recommande donc que la logique métier ne génère pas directement le texte des messages de journal. Au lieu de cela, déléguez-le à un objet de journalisation.
implements
aninterface
, qui décrit l'API interne que votre logique métier peut utiliser.interface
.Vous pouvez ensuite tester que vos classes de logique métier informent correctement l'interface de journalisation des événements, en créant un faux journal, qui implémente l'API de journalisation interne, et en utilisant l'injection de dépendances dans la phase de configuration de votre test.
Comme ça:
la source
Ce que j'ai fait si tout ce que je veux faire, c'est de voir qu'une chaîne a été enregistrée (au lieu de vérifier les instructions de journal exactes qui est tout simplement trop fragile), c'est de rediriger StdOut vers un tampon, de faire un contient, puis de réinitialiser StdOut:
la source
java.util.logging
(bien que j'aie utiliséSystem.setErr(new PrintStream(buffer));
, car il se connecte à stderr), mais cela ne fonctionne pas (le tampon reste vide). si j'utiliseSystem.err.println("foo")
directement, cela fonctionne, donc je suppose que le système de journalisation conserve sa propre référence du flux de sortie, d'où il provientSystem.err
, donc mon appel àSystem.setErr(..)
n'a aucun effet sur la sortie du journal, comme cela se produit après l'initialisation du système de journalisation.J'ai répondu à une question similaire pour log4j voir comment-puis-je-tester-avec-junit-that-a-warning-was-logged-with-log4
Ceci est plus récent et exemple avec Log4j2 (testé avec 2.11.2) et junit 5;
Utilisation des dépendances Maven suivantes
la source
Si vous utilisez log4j2, la solution de https://www.dontpanicblog.co.uk/2018/04/29/test-log4j2-with-junit/ m'a permis d'affirmer que les messages étaient enregistrés.
La solution va comme ceci:
Définir un appender log4j en tant que règle ExternalResource
Définissez un test qui utilise votre règle ExternalResource
N'oubliez pas que log4j2.xml fait partie de src / test / resources
la source