在java中的mockito的记录器

我尝试用mokito验证记录器消息。
但是,我无法运行我的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;
    }
}

==========================================

我的班级

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());
    }
}

错误:

通缉但未被调用:mockAppender.doAppend(); - >在AppTest.testWithOver3Element(AppTest.java:52)实际上,这个模拟与零交互。

在sun.reflect.NativeMethodAccessorImpl.invoke(未知源)处的sun.reflect.DelegatingMethodAccessorImpl.invoke(未知源)处,使用sun.reflect.NativeMethodAccessorImpl.invoke0(本地方法)在AppTest.testWithOver3Element(AppTest.java:52) lang.reflect.Method.invoke(Unknown Source)at org.junit.runners.model.FrameworkMethod $ 1.runReflectiveCall(FrameworkMethod.java:47)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java: 12)在org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)位于org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.junit.internal.runners .statements.RunBefores.evaluate(RunBefores.java:26)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)at org。 junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)at org.junit.runners.ParentRunner $ 3.run(Paren tRunner.java:238)在org.junit.runners.ParentRunner $ 1.schedule(ParentRunner.java:63)位于org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)的org.junit.runners.ParentRunner。在org.junit.runners.ParentRunner上访问$ 000(ParentRunner.java:53)$ 2.evaluate(ParentRunner.java:229)at org.junit.runners.ParentRunner.run(ParentRunner.java:309)at org.mockito.internal .runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java :86)at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)。 jdt.inte rnal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)


有几件事你可以改善你的代码:

  • 切换到slf4j。 这将允许你编码到一个接口,并且不知道下面的日志实现(你的情况下是apache log4j)。

  • 切换到slf4j将允许您在传递到日志框架时不必连接字符串 - 例如:

  • 这个语句:LOGGER.debug(“列表已经有{} entries”+ list.size());
  • 可以这样写:LOGGER.debug(“该列表已经有{} entries”,list.size());
  • 这还有一个好处,就是让字符串文字中的占位符实际上起作用。

  • 您正尝试通过Logging框架间接断言并捕获对象的调用。 这将会很脆弱并且容易出错,因为你永远不知道在Logging框架内部会做什么样的调用。 只模拟你的直接依赖关系。

  • 不要测试日志记录。 这不是完全可见的类的行为,它使测试变得脆弱和复杂。 另外,像使用ArrayList(即语言的一部分)那样对待日志语句,可以充分利用它们,并将信息输出到控制台,这可能有助于调试失败的测试。 脆弱的一个例子是,如果您更改日志记录语句以添加更多信息,或者您可能会向该方法添加另一个日志记录语句,则此测试可能会因没有理由而中断。 至少不要声称被调用的次数,因为这将是非常脆弱的。

  • 所有这一切都说,如果您必须测试与Logging框架的交互 - 这是您的代码的修改版本,它运行并提供相同的功能。 这基本上是选项3中的改进列表 -

    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;
            }
        }
    }
    

    你也可以看看像PowerMockito这样的软件包来模拟静态 - 但只有在绝对需要的时候。

    希望这可以帮助。


    你的appender没有被解雇,因为你已经将它设置为INFO级别,但是你的代码正在调用LOGGER.debugDEBUG是比INFO更低级别的日志记录,所以你的appender将不会看到它。


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

    测试传递一个包含3个元素的列表,并且生产代码表示这意味着添加第二个参数。 但是你的测试用例期望它被添加!

    因此,这里首先要确保您的测试真正覆盖您在您展示的产品代码中描绘的行为。

    然后我想知道为什么你的测试在这里使用真正的记录器。 我认为你提供一个模拟记录器并指定预期的调用会容易得多!

    链接地址: http://www.djcxy.com/p/90369.html

    上一篇: Logger with mockito in java

    下一篇: Null Pointer Exception at Junit test with fileSystem and spring boot