java中带有mockito的记录器

java中带有mockito的记录器,java,unit-testing,mockito,log4j,Java,Unit Testing,Mockito,Log4j,我正在尝试用mockito验证日志记录程序消息 但是,我不能运行junit类来覆盖所有代码行 你知道原因吗 我的代码: public class App { private static final Logger LOGGER = Logger.getLogger(App.class); public List<String> addToListIfSizeIsUnder3(final List<String> list, final S

我正在尝试用mockito验证日志记录程序消息

但是,我不能运行junit类来覆盖所有代码行

你知道原因吗

我的代码:

    public class App {
      private static final Logger LOGGER = Logger.getLogger(App.class);

      public List<String> addToListIfSizeIsUnder3(final List<String> list, final String value) {
        if (list == null) {
            LOGGER.error("A null list was passed in");
            return null;
        }
        if (list.size() < 3) {
            list.add(value);
        } else {
            LOGGER.debug("The list already has {} entries"+ list.size());
        }
        return list;
    }
}
公共类应用程序{
私有静态最终记录器=Logger.getLogger(App.class);
公共列表addToListifSizeUnder3(最终列表,最终字符串值){
if(list==null){
LOGGER.error(“传入了一个空列表”);
返回null;
}
if(list.size()<3){
列表。添加(值);
}否则{
debug(“列表已经有{}个条目”+list.size());
}
退货清单;
}
}
我的初中班
导入静态org.mockito.mockito.times;
导入静态org.mockito.mockito.verify;
导入java.util.ArrayList;
导入java.util.List;
导入org.apache.log4j.Appender;
导入org.apache.log4j.Level;
导入org.apache.log4j.Logger;
导入org.apache.log4j.spi.LoggingEvent;
导入org.junit.Assert;
导入org.junit.Before;
导入org.junit.Test;
导入org.junit.runner.RunWith;
导入org.mockito.ArgumentCaptor;
导入org.mockito.Captor;
导入org.mockito.Mock;
导入org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
公共类应用程序测试{
私有应用程序uut;
@嘲弄
私人追加器模拟追加器;
@俘虏
私人辩论CaptorCaptorLoggingEvent;
@以前
公共作废设置(){
uut=新应用程序();
Logger root=Logger.getRootLogger();
root.addAppender(mockAppender);
root.setLevel(Level.INFO);
}
/**
*我想用3个以上的元素进行测试。
*/
@试验
公共void testWithOver3Element(){
List myList=new ArrayList();
myList.add(“价值1”);
myList.add(“价值2”);
myList.add(“价值3”);
myList.add(“价值4”);
List outputList=uut.addToListIfSizeIsUnder3(myList,“some value”);
Assert.assertEquals(4,outputList.size());
Assert.assertFalse(myList.contains(“某些值”));
试一试{
验证(mockAppender,times(1)).doAppend(captorLoggingEvent.capture());
}捕获(断言错误){
e、 printStackTrace();
}
LoggingEvent LoggingEvent=captorLoggingEvent.getAllValues().get(0);
Assert.assertEquals(“列表已经有{}个条目”,loggingEvent.getMessage());
Assert.assertEquals(Level.DEBUG,loggingEvent.getLevel());
}
}
错误:

需要但未调用:mockAppender.doAppend(); ->实际上,在AppTest.testWithOver3Element(AppTest.java:52)中,没有与此模拟的交互

在AppTest.testWithOver3Element(AppTest.java:52)处 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)位于 位于的sun.reflect.NativeMethodAccessorImpl.invoke(未知源) sun.reflect.DelegatingMethodAccessorImpl.invoke(未知源)位于 java.lang.reflect.Method.invoke(未知源代码)位于 org.junit.runners.model.FrameworkMethod$1.runReflectVeCall(FrameworkMethod.java:47) 在 org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 在 org.junit.runners.model.FrameworkMethod.invokeeexplosive(FrameworkMethod.java:44) 在 org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 在 org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) 位于org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) 在 org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) 位于org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)位于 org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)位于 org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)位于 org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)位于 org.junit.runners.ParentRunner.run(ParentRunner.java:309)位于 org.mockito.internal.runners.junit45和higherrunnerimpl.run(junit45和higherrunnerimpl.java:37) 在 org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62) 在 org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) 在 org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)


您的appender没有被解雇,因为您已将其设置为
INFO
级别,但您的代码正在调用
LOGGER.debug
DEBUG
的日志记录级别低于
INFO
,因此您的appender不会看到它

您的问题是测试和生产代码之间存在不一致

测试通过了一个包含3个元素的列表,生产代码表明这意味着第二个参数是未添加的。但是您的测试用例希望添加它

因此,这里的第一件事是确保您的测试真正涵盖了您在所展示的生产代码中描述的行为


然后我想知道为什么您的测试在这里与一个真正的记录器一起工作;我认为提供一个模拟的
记录器并指定它的预期调用会容易得多

您可以做几件事来改进代码:

  • 切换到slf4j。这将允许您编写接口代码,并且不知道底层的日志实现(ApacheL
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.log4j.Appender;
    import org.apache.log4j.Level;
    import org.apache.log4j.Logger;
    import org.apache.log4j.spi.LoggingEvent;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.ArgumentCaptor;
    import org.mockito.Captor;
    import org.mockito.Mock;
    import org.mockito.runners.MockitoJUnitRunner;
    
    @RunWith(MockitoJUnitRunner.class)
    public class AppTest {
        private App uut;
        @Mock
        private Appender mockAppender;
        @Captor
        private ArgumentCaptor<LoggingEvent> captorLoggingEvent;
    
        @Before
        public void setup() {
            uut = new App();
    
            Logger root = Logger.getRootLogger();
            root.addAppender(mockAppender);
            root.setLevel(Level.INFO);
        }
    
        /**
         * I want to test with over 3 elements.
         */
        @Test
        public void testWithOver3Element() {
            List<String> myList = new ArrayList<String>();
            myList.add("value 1");
            myList.add("value 2");
            myList.add("value 3");
            myList.add("value 4");
            List<String> outputList = uut.addToListIfSizeIsUnder3(myList, "some value");
            Assert.assertEquals(4, outputList.size());
            Assert.assertFalse(myList.contains("some value"));
    
    try {
                verify(mockAppender, times(1)).doAppend(captorLoggingEvent.capture());
            } catch (AssertionError e) {
                e.printStackTrace();
            }
    
            LoggingEvent loggingEvent = captorLoggingEvent.getAllValues().get(0);
            Assert.assertEquals("The list already has {} entries", loggingEvent.getMessage());
            Assert.assertEquals(Level.DEBUG, loggingEvent.getLevel());
        }
    }
    
    package com.spring.mockito;
    
    import static org.mockito.Mockito.spy;
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    import static org.mockito.Mockito.when;
    
    import org.apache.log4j.Logger;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.Mock;
    import org.mockito.Mockito;
    import org.mockito.runners.MockitoJUnitRunner;
    
    import java.util.Arrays;
    import java.util.List;
    
    @RunWith(MockitoJUnitRunner.class)
    public class AppTest {
    
        // create a mock of the logger
        @Mock
        private Logger logger;
    
        private App uut;
    
        // Not needed - dont test something that gets called through something else
        // @Captor
        // private ArgumentCaptor<LoggingEvent> captorLoggingEvent;
    
        @Before
        public void setup() {
            // spy the class under test so we can override the logger method to return our mock logger
            uut = spy(new App());
            when(uut.logger()).thenReturn(logger);
    
            // Not needed test with the mock directly.
            // Logger root = Logger.getRootLogger();
            // root.addAppender(mockAppender);
            // root.setLevel(Level.DEBUG);
        }
    
        /**
         * I want to test with over 3 elements.
         */
        @Test
        public void testWithOver3Element() {
            List<String> myList = Arrays.asList("value 1", "value 2", "value 3", "value 4");
    
            List<String> outputList = uut.addToListIfSizeIsUnder3(myList, "some value");
    
            Assert.assertEquals(4, outputList.size());
            Assert.assertFalse(myList.contains("some value"));
            verify(logger, times(1)).debug("The list already has {} entries4");
    
            // not needed
            // try {
            // verify(mockAppender, times(1)).doAppend(captorLoggingEvent.capture());
            // } catch (AssertionError e) {
            // e.printStackTrace();
            // }
            //
            // LoggingEvent loggingEvent = captorLoggingEvent.getAllValues().get(0);
            // Assert.assertEquals("The list already has {} entries", loggingEvent.getMessage());
            // Assert.assertEquals(Level.DEBUG, loggingEvent.getLevel());
        }
    
        public static class App {
            private static final Logger LOGGER = Logger.getLogger(App.class);
    
            public List<String> addToListIfSizeIsUnder3(final List<String> list, final String value) {
                if (list == null) {
                    logger().error("A null list was passed in");
                    return null;
                }
                if (list.size() < 3) {
                    list.add(value);
                } else {
                    // if you use slf4j this concatenation is not needed
                    logger().debug("The list already has {} entries" + list.size());
                }
                return list;
            }
    
            // make a package private method for testing purposes to allow you to inject a mock
            Logger logger() {
                return LOGGER;
            }
        }
    }
    
    class MyClass{
    
        public Logger logger = LoggerFactory.getLogger(MyClass.class);
        //other your fields and methods
    }
    
    
    class MyTestClass{
        private MyClass myClass = mock(MyClass.class);
        private Logger log = mock(Logger.class);
    
    
        @Before
        public void init() {
            myClass.logger = log;
        }
    
    
         @Test
         public void firstTest() {
            doNothing().when(log).debug(any());
         }
    }