Saya membutuhkan ini beberapa kali juga. Saya telah mengumpulkan sampel kecil di bawah ini, yang ingin Anda sesuaikan dengan kebutuhan Anda. Pada dasarnya, Anda membuat sendiri Appender
dan menambahkannya ke logger yang Anda inginkan. Jika Anda ingin mengumpulkan semuanya, root logger adalah tempat yang baik untuk memulai, tetapi Anda bisa menggunakan yang lebih spesifik jika Anda mau. Jangan lupa untuk menghapus Appender setelah selesai, jika tidak, Anda dapat membuat kebocoran memori. Di bawah ini saya sudah melakukannya dalam ujian, tetapi setUp
atau @Before
dan tearDown
atau @After
mungkin tempat yang lebih baik, tergantung pada kebutuhan Anda.
Juga, implementasi di bawah ini mengumpulkan semua yang ada di List
dalam memori. Jika Anda banyak log, Anda mungkin mempertimbangkan untuk menambahkan filter untuk menghapus entri yang membosankan, atau untuk menulis log ke file sementara pada disk (Petunjuk: LoggingEvent
adalah Serializable
, jadi Anda harus bisa membuat serialisasi objek acara, jika pesan log Anda adalah.)
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);
}
}
logger.getAllAppenders()
, lalu melangkah maju dan memanggilappender.setThreshold(Level.OFF)
masing-masing (dan mengatur ulang mereka ketika Anda selesai!). Ini memastikan bahwa pesan "buruk" yang Anda coba hasilkan tidak muncul di log pengujian dan membuat takut pengembang berikutnya.ListAppender<ILoggingEvent>
alih-alih membuat append kustom Anda sendiri.Logger
keorg.apache.logging.log4j.core.Logger
(kelas implementasi untuk antarmuka) Anda akan mendapatkan aksessetAppender()/removeAppender()
lagi.Berikut ini adalah solusi Logback sederhana dan efisien.
Tidak perlu menambah / membuat kelas baru.
Itu bergantung
ListAppender
: appender logback whitebox di mana entri log ditambahkan dalampublic List
bidang yang bisa kita gunakan untuk membuat pernyataan kami.Ini adalah contoh sederhana.
Kelas foo:
Kelas FooTest:
Pernyataan JUnit tidak terdengar sangat disesuaikan untuk menegaskan beberapa properti spesifik elemen daftar.
Pustaka pencocokan / pernyataan sebagai AssertJ atau Hamcrest tampak lebih baik untuk itu:
Dengan AssertJ itu akan menjadi:
sumber
mock
kelas yang sedang diuji. Anda harus instantiate dengannew
operatorTerima kasih banyak atas jawaban yang cepat dan bermanfaat ini; mereka menempatkan saya di jalan yang benar untuk solusi saya.
Basis kode yang saya ingin gunakan ini, menggunakan java.util.logging sebagai mekanisme logger, dan saya tidak merasa cukup di rumah dalam kode-kode untuk benar-benar mengubahnya ke log4j atau untuk logger antarmuka / fasad. Tapi berdasarkan saran ini, saya 'meretas' ekstensi julhandler dan itu berfungsi sebagai hadiah.
Ringkasan singkat berikut. Perpanjang
java.util.logging.Handler
:Jelas, Anda dapat menyimpan sebanyak yang Anda suka / inginkan / butuhkan dari
LogRecord
, atau mendorong semuanya ke dalam tumpukan sampai Anda mendapatkan kelebihan.Dalam persiapan untuk uji junit, Anda membuat
java.util.logging.Logger
dan menambahkan yang baruLogHandler
ke dalamnya:Panggilan untuk
setUseParentHandlers()
adalah untuk membungkam para penangan normal, sehingga (untuk uji coba uji coba ini) tidak terjadi penebangan yang tidak perlu. Lakukan apa pun yang perlu diuji-kode oleh Anda untuk menggunakan logger ini, jalankan tes dan tegaskan Kesetaraan:(Tentu saja, Anda akan memindahkan sebagian besar pekerjaan ini ke dalam
@Before
metode dan membuat berbagai macam perbaikan lainnya, tetapi itu akan mengacaukan presentasi ini.)sumber
Pilihan lain adalah dengan mengejek Appender dan memverifikasi apakah pesan telah masuk ke appender ini. Contoh untuk Log4j 1.2.x dan mockito:
sumber
Secara efektif Anda menguji efek samping dari kelas dependen. Untuk pengujian unit, Anda hanya perlu memverifikasi itu
dipanggil dengan parameter yang benar. Karenanya gunakan kerangka kerja mengejek untuk meniru logger dan itu akan memungkinkan Anda untuk menguji perilaku kelas Anda sendiri.
sumber
Mengejek adalah sebuah pilihan di sini, walaupun itu akan sulit, karena para penebang pada umumnya adalah final statis pribadi - jadi pengaturan tiruan logger tidak akan menjadi sepotong kue, atau akan memerlukan modifikasi kelas yang sedang diuji.
Anda dapat membuat Appender khusus (atau apa pun namanya), dan mendaftarkannya - baik melalui file konfigurasi hanya tes, atau runtime (dengan cara, tergantung pada kerangka kerja logging). Dan kemudian Anda bisa mendapatkan appender itu (baik secara statis, jika dideklarasikan dalam file konfigurasi, atau dengan referensi saat ini, jika Anda memasukkannya runtime), dan memverifikasi isinya.
sumber
Terinspirasi oleh solusi @ RonaldBlaschke, saya datang dengan ini:
... yang memungkinkan Anda melakukan:
Anda mungkin bisa membuatnya menggunakan hamcrest dengan cara yang lebih cerdas, tapi saya sudah membiarkannya.
sumber
Untuk log4j2 solusinya sedikit berbeda karena AppenderSkeleton tidak tersedia lagi. Selain itu, menggunakan Mockito, atau pustaka sejenis untuk membuat Appender dengan ArgumentCaptor tidak akan berfungsi jika Anda mengharapkan beberapa pesan logging karena MutableLogEvent digunakan kembali pada beberapa pesan log. Solusi terbaik yang saya temukan untuk log4j2 adalah:
sumber
Seperti disebutkan dari yang lain, Anda bisa menggunakan kerangka mengejek. Agar ini berfungsi, Anda harus mengekspos logger di kelas Anda (walaupun saya lebih suka membuatnya menjadi paket pribadi daripada membuat setter publik).
Solusi lainnya adalah membuat logger palsu dengan tangan. Anda harus menulis logger palsu (kode fixture lebih banyak) tetapi dalam kasus ini saya lebih suka peningkatan keterbacaan tes terhadap kode yang disimpan dari kerangka kerja mengejek.
Saya akan melakukan sesuatu seperti ini:
sumber
Wow. Saya tidak yakin mengapa ini sangat sulit. Saya menemukan saya tidak dapat menggunakan contoh kode di atas karena saya menggunakan log4j2 lebih dari slf4j. Ini solusi saya:
sumber
Inilah yang saya lakukan untuk logback.
Saya membuat kelas TestAppender:
Kemudian di induk dari kelas tes unit tes saya, saya membuat metode:
Saya memiliki file logback-test.xml yang didefinisikan dalam src / test / resources dan saya menambahkan appender test:
dan menambahkan append ini ke root append:
Sekarang di kelas tes saya yang diperluas dari kelas tes orang tua saya, saya bisa mendapatkan appender dan mendapatkan pesan terakhir yang dicatat dan memverifikasi pesan, tingkat, yang bisa dibuang.
sumber
Untuk Junit 5 (Jupiter) Spring's OutputCaptureExtension cukup berguna. Ini tersedia sejak Spring Boot 2.2 dan tersedia di artefak uji pegas boot .
Contoh (diambil dari javadoc):
sumber
getOut()
ataugetErr()
.Sedangkan bagi saya, Anda dapat menyederhanakan tes Anda dengan menggunakan
JUnit
denganMockito
. Saya mengusulkan solusi berikut untuk itu:Itu sebabnya kami memiliki fleksibilitas yang bagus untuk pengujian dengan jumlah pesan yang berbeda
sumber
when(appender.isStarted()).thenReturn(true); when(appender.getName()).thenReturn("Test Appender");
dan ubah LoggingEvent -> LogEventsumber
API untuk Log4J2 sedikit berbeda. Anda juga mungkin menggunakan aplikasi async. Saya membuat append latch untuk ini:
Gunakan seperti ini:
sumber
Perhatikan bahwa di Log4J 2.x, antarmuka publik
org.apache.logging.log4j.Logger
tidak termasuksetAppender()
danremoveAppender()
metode.Tetapi jika Anda tidak melakukan sesuatu yang terlalu mewah, Anda harus dapat melemparkannya ke kelas implementasi
org.apache.logging.log4j.core.Logger
, yang mengekspos metode tersebut.Berikut ini contoh dengan Mockito dan AssertJ :
sumber
Gagasan lain yang layak disebut, meskipun ini adalah topik yang lebih lama, adalah menciptakan produser CDI untuk menyuntikkan logger Anda sehingga mengejek menjadi mudah. (Dan itu juga memberi keuntungan karena tidak harus mendeklarasikan "pernyataan logger keseluruhan" lagi, tapi itu di luar topik)
Contoh:
Membuat logger untuk disuntikkan:
Kualifikasi:
Menggunakan logger dalam kode produksi Anda:
Menguji logger dalam kode pengujian Anda (memberikan contoh easyMock):
sumber
Menggunakan Jmockit (1.21) saya bisa menulis tes sederhana ini. Tes ini memastikan pesan ERROR tertentu dipanggil hanya sekali.
sumber
Mengejek Appender dapat membantu menangkap garis log. Temukan sampel di: http://clearqa.blogspot.co.uk/2016/12/test-log-lines.html
sumber
Gunakan kode di bawah ini. Saya menggunakan kode yang sama untuk tes integrasi pegas saya di mana saya menggunakan log kembali untuk logging. Gunakan metode assertJobIsScheduled untuk menegaskan teks yang dicetak dalam log.
sumber
jika Anda menggunakan
java.util.logging.Logger
artikel ini mungkin sangat membantu, itu menciptakan penangan baru dan membuat pernyataan tentang log Output: http://octodecillion.com/blog/jmockit-test-logging/sumber
Ada dua hal yang mungkin ingin Anda coba.
Kedua hal tersebut sebenarnya adalah hal yang berbeda, sehingga dapat diuji secara terpisah. Namun, pengujian yang kedua (teks pesan) sangat bermasalah, saya sarankan untuk tidak melakukannya sama sekali. Pengujian teks pesan pada akhirnya akan terdiri dari pengecekan bahwa satu string teks (teks pesan yang diharapkan) sama dengan, atau dapat diperoleh dari, string teks yang digunakan dalam kode logging Anda.
Perhatikan bahwa memiliki kode program Anda (menerapkan logika bisnis, mungkin) secara langsung memanggil antarmuka pembuatan log teks adalah desain yang buruk (tapi sayangnya sangat umum). Kode yang bertanggung jawab untuk logika bisnis juga memutuskan beberapa kebijakan pencatatan dan teks pesan log. Ini mencampur logika bisnis dengan kode antarmuka pengguna (ya, pesan log adalah bagian dari antarmuka pengguna program Anda). Hal-hal itu harus terpisah.
Karena itu saya merekomendasikan bahwa logika bisnis tidak secara langsung menghasilkan teks pesan log. Alih-alih minta didelegasikan ke objek logging.
implements
sebuahinterface
, yang menggambarkan API internal yang logika bisnis Anda dapat menggunakan.interface
.Anda kemudian dapat menguji bahwa kelas logika bisnis Anda dengan benar memberi tahu antarmuka pencatatan tentang peristiwa, dengan membuat mock logger, yang mengimplementasikan API pencatatan internal, dan menggunakan injeksi ketergantungan pada fase pengaturan pengujian Anda.
Seperti ini:
sumber
Apa yang telah saya lakukan jika semua yang ingin saya lakukan adalah melihat bahwa beberapa string telah dicatat (tidak seperti memverifikasi pernyataan log yang terlalu rapuh) adalah untuk mengarahkan StdOut ke buffer, melakukan isi, lalu reset StdOut:
sumber
java.util.logging
(meskipun saya gunakanSystem.setErr(new PrintStream(buffer));
, karena log ke stderr), tetapi tidak berfungsi (buffer tetap kosong). jika saya menggunakanSystem.err.println("foo")
secara langsung, ia berfungsi, jadi saya berasumsi bahwa sistem logging menyimpan referensi sendiri dari aliran output, yang diambil darinyaSystem.err
, jadi panggilan saya untukSystem.setErr(..)
tidak berpengaruh pada output log, seperti yang terjadi setelah sistem log init.Saya menjawab pertanyaan serupa untuk log4j lihat bagaimana-bisa-saya-uji-dengan-junit-bahwa-peringatan-adalah-login-dengan-log4
Ini lebih baru dan contoh dengan Log4j2 (diuji dengan 2.11.2) dan junit 5;
Menggunakan dependensi pakar berikut
sumber
Jika Anda menggunakan log4j2, solusi dari https://www.dontpanicblog.co.uk/2018/04/29/test-log4j2-with-junit/ memungkinkan saya untuk menegaskan bahwa pesan telah dicatat.
Solusinya seperti ini:
Tetapkan app4 log app sebagai aturan ExternalResource
Tetapkan tes yang menggunakan aturan ExternalResource Anda
Jangan lupa untuk memiliki log4j2.xml sebagai bagian dari src / test / resources
sumber