Шаблоны URL являются массивом, поэтому вам нужно передать URL как элемент массива. Пример: -
urlpatterns = [url(), url(), url()]
Большое спасибо за эти (на удивление) быстрые и полезные ответы; они направили меня на правильный путь к моему решению.
Кодовая база, в которой я хочу это использовать, использует java.util.logging в качестве механизма регистрации, и я не чувствую себя достаточно хорошо в этих кодах, чтобы полностью изменить это к log4j или к интерфейсам / фасадам регистратора. Но, основываясь на этих предложениях, я «взломал» расширение julhandler, и это работает как удовольствие.
Ниже следует краткое резюме. Extend java.util.logging.Handler
:
class LogHandler extends Handler
{
Level lastLevel = Level.FINEST;
public Level checkLevel() {
return lastLevel;
}
public void publish(LogRecord record) {
lastLevel = record.getLevel();
}
public void close(){}
public void flush(){}
}
Очевидно, что вы можете хранить столько, сколько хотите / хотите / нужно из LogRecord
, или помещать их все в стек, пока вы получите переполнение.
При подготовке к junit-test вы создаете java.util.logging.Logger
и добавляете к нему такой новый LogHandler
:
@Test tester() {
Logger logger = Logger.getLogger("my junit-test logger");
LogHandler handler = new LogHandler();
handler.setLevel(Level.ALL);
logger.setUseParentHandlers(false);
logger.addHandler(handler);
logger.setLevel(Level.ALL);
Вызов setUseParentHandlers ()
предназначен для отключения обычных обработчиков, чтобы (для этого запуска junit-test) не происходило ненужного ведения журнала. Сделайте все, что нужно вашему тестируемому коду, чтобы использовать этот регистратор, запустите тест и assertEquality:
libraryUnderTest.setLogger(logger);
methodUnderTest(true); // see original question.
assertEquals("Log level as expected?", Level.INFO, handler.checkLevel() );
}
(Конечно, вы бы переместили большую часть этой работы в метод @Before
и сделали другие улучшения, но это загромождает эту презентацию.)
Here is the sample code to mock log, irrespective of the version used for junit or sping, springboot.
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
import org.mockito.ArgumentMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.Test;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class MyTest {
private static Logger logger = LoggerFactory.getLogger(MyTest.class);
@Test
public void testSomething() {
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
final Appender mockAppender = mock(Appender.class);
when(mockAppender.getName()).thenReturn("MOCK");
root.addAppender(mockAppender);
//... do whatever you need to trigger the log
verify(mockAppender).doAppend(argThat(new ArgumentMatcher() {
@Override
public boolean matches(final Object argument) {
return ((LoggingEvent)argument).getFormattedMessage().contains("Hey this is the message I want to see");
}
}));
}
}
Я ответил на подобный вопрос для log4j, видят how-can-i-test-with-junit-that-a-warning-was-logged-with-log4
, Это является более новым и пример с Log4j2 (протестированный с 2.11.2) и junit 5;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
class TestLogger {
private TestAppender testAppender;
private LoggerConfig loggerConfig;
private final Logger logger = (Logger)
LogManager.getLogger(ClassUnderTest.class);
@Test
@DisplayName("Test Log Junit5 and log4j2")
void test() {
ClassUnderTest.logMessage();
final LogEvent 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().toString()
,"Hello Test");
}
@BeforeEach
private void setup() {
testAppender = new TestAppender("test", null);
final LoggerContext context = logger.getContext();
final Configuration configuration = context.getConfiguration();
loggerConfig = configuration.getLoggerConfig(logger.getName());
loggerConfig.setLevel(Level.INFO);
loggerConfig.addAppender(testAppender,Level.INFO,null);
testAppender.start();
context.updateLoggers();
}
@AfterEach
void after(){
logAppender.stop();
loggerConfig.removeAppender("TestAppender");
final LoggerContext context = logger.getContext();
context.updateLoggers();
}
@Plugin( name = "TestAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE)
static class TestAppender extends AbstractAppender {
List<LogEvent> events = new ArrayList();
protected TestAppender(String name, Filter filter) {
super(name, filter, null);
}
@PluginFactory
public static TestAppender createAppender(
@PluginAttribute("name") String name,
@PluginElement("Filter") Filter filter) {
return new TestAppender(name, filter);
}
@Override
public void append(LogEvent event) {
events.add(event);
}
}
static class ClassUnderTest {
private static final Logger LOGGER = (Logger) LogManager.getLogger(ClassUnderTest.class);
public static void logMessage(){
LOGGER.info("Hello Test");
LOGGER.debug("Hello Test");
}
}
}
Mocking - это вариант, хотя это было бы сложно, потому что логгеры обычно являются закрытыми статическими final - поэтому установка фиктивного логгера не будет делом торта, либо потребуется модификация тестируемого класса.
Вы можете создать собственный Appender (или как он там называется) и зарегистрировать его - либо через тестовый файл конфигурации, либо через среду выполнения (в некотором роде, в зависимости от на каркасе регистрации). Затем вы можете получить это приложение (статически, если оно объявлено в файле конфигурации, или по его текущей ссылке, если вы подключаете его во время выполнения) и проверить его содержимое.
Фактически вы тестируете побочный эффект зависимого класса. Для модульного тестирования вам нужно только убедиться, что
logger.info ()
был вызван с правильным параметром. Следовательно, используйте фреймворк для имитации логгера, который позволит вам протестировать поведение вашего собственного класса.
Как уже упоминалось, вы можете использовать фреймворк для фиксации. Чтобы это заработало, вы должны открыть регистратор в своем классе (хотя я, вероятно, предпочел бы сделать его закрытым пакетом вместо создания общедоступного установщика).
Другое решение - создать поддельный регистратор вручную. Вы должны написать фальшивый регистратор (больше кода фикстуры), но в этом случае я бы предпочел улучшенную читаемость тестов по сохраненному коду из фреймворка имитатора.
Я бы сделал что-то вроде этого:
class FakeLogger implements ILogger {
public List<String> infos = new ArrayList<String>();
public List<String> errors = new ArrayList<String>();
public void info(String message) {
infos.add(message);
}
public void error(String message) {
errors.add(message);
}
}
class TestMyClass {
private MyClass myClass;
private FakeLogger logger;
@Before
public void setUp() throws Exception {
myClass = new MyClass();
logger = new FakeLogger();
myClass.logger = logger;
}
@Test
public void testMyMethod() {
myClass.myMethod(true);
assertEquals(1, logger.infos.size());
}
}
Мне это тоже нужно было несколько раз. Ниже я собрал небольшой образец, который вы хотите приспособить к своим потребностям. По сути, вы создаете свой собственный Appender
и добавляете его в желаемый регистратор. Если вы хотите собрать все, корневое средство ведения журнала - хорошее место для начала, но вы можете использовать более конкретный, если хотите. Не забудьте удалить Appender, когда закончите, иначе вы можете создать утечку памяти. Ниже я сделал это в рамках теста, но setUp
или @Before
и tearDown
или @After
могут быть лучше, в зависимости от ваших потребностей.
Кроме того, реализация ниже собирает все в List
в памяти. Если вы много ведете журнал, вы можете подумать о добавлении фильтра, чтобы удалить скучные записи или записать журнал во временный файл на диске (Подсказка: LoggingEvent
is Serializable
, поэтому вы должен иметь возможность просто сериализовать объекты событий, если ваше сообщение журнала.)
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);
}
}