我是否可以在启用所有日志记录的情况下自动执行一次 JUnit 测试用例,并在禁用所有日志记录的情况下自动执行一次 JUnit 测试用例?

发布于 2024-07-23 06:36:14 字数 1302 浏览 14 评论 0原文

我找到了解决方案,请参阅下面我自己的答案。 有人有更优雅的吗?

我想这样做是为了增加代码覆盖率并发现细微的错误。

假设要测试以下类:

public class Foo {
    private final Logger logger = LoggerFactory.getLogger(Foo.class);
    public void bar() {
        String param=[..];
        if(logger.isInfoEnabled()) logger.info("A message with parameter {}", param);

        if(logger.isDebugEnabled()) {
            // some complicated preparation for the debug message
            logger.debug([the debug message]);
        }
    }
}

和以下测试类:

public class FooTest {
    @Test
    public void bar() {
        Foo foo=new Foo();
        foo.bar();
    }
}

像 Cobertura 这样的代码覆盖工具将正确报告仅检查了某些条件分支。

记录器的信息和调试要么被激活,要么被停用。

除了你的覆盖分数看起来很糟糕之外,这还带来了真正的风险。

如果 if(logger.isDebugEnabled()) 内部的代码引起一些副作用怎么办? 如果您的代码仅在启用 DEBUG 时才工作,并且在日志级别设置为 INFO 时严重失败,该怎么办? (这实际上发生在我们的一个项目中:p)

所以我的结论是,包含记录器语句的代码应该始终在启用所有日志记录的情况下测试一次,并在禁用所有日志记录的情况下测试一次......

有没有办法用 JUnit 做类似的事情? 我知道如何全局启用或禁用 Logback 中的所有日志记录,因此问题是:如何执行测试两次,一次启用日志记录,一次禁用日志记录。

ps我知道这个问题但我不认为这是重复的。 我不太关心绝对覆盖率值,而是关心 if(logger.isDebugEnabled()) 中可能包含的微妙的、难以发现的错误。

I've found a solution, see my own answer below. Does anyone have a more elegant one?

I want to do this to increase code-coverage and find subtle bugs.

Assume the following class to be tested:

public class Foo {
    private final Logger logger = LoggerFactory.getLogger(Foo.class);
    public void bar() {
        String param=[..];
        if(logger.isInfoEnabled()) logger.info("A message with parameter {}", param);

        if(logger.isDebugEnabled()) {
            // some complicated preparation for the debug message
            logger.debug([the debug message]);
        }
    }
}

and the following test-class:

public class FooTest {
    @Test
    public void bar() {
        Foo foo=new Foo();
        foo.bar();
    }
}

A code-coverage tool like e.g. Cobertura will correctly report that only some of the conditional branches have been checked.

info and debug are either activated or deactivated for the logger.

Besides looking bad in your coverage score, this poses a real risk.

What if there is some side effect caused by code inside if(logger.isDebugEnabled())? What if your code does only work if DEBUG is enabled and fails miserably if the log level is set to INFO? (This actually happened in one of our projects :p)

So my conclusion is that code containing logger statements should always be tested once with all logging enabled and once with all logging disabled...

Is there a way to do something like that with JUnit? I know how to globally enable or disable all my logging in Logback so the problem is: How can I execute the tests twice, once with logging enabled, once with logging disabled.

p.s. I'm aware of this question but I don't think this is a duplicate. I'm less concerned about the absolute coverage values but about subtle, hard-to-find bugs that might be contained inside of a if(logger.isDebugEnabled()).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

幻想少年梦 2024-07-30 06:36:14

我通过实现一个基类解决了这个问题,如果需要这样的功能,测试类应该扩展该基类。

文章编写参数化 JUnit 测试包含解决方案。

请参阅 LoggingTestBase 用于日志记录基类和 LoggingTestBaseExampleTest 使用它的简单示例。

每个包含的测试方法都会执行三次:

1. 它像往常一样使用 logback-test.xml 中定义的日志记录来执行。 这应该有助于编写/调试测试。

2. 它是在启用所有日志记录并写入文件的情况下执行的。 测试后该文件被删除。

3. 它是在禁用所有日志记录的情况下执行的。

是的,LoggingTestBase 需要文档;)

I've solved this problem by implementing a base class that test classes should extend if such functionality is desired.

The article Writing a parameterized JUnit test contained the solution.

See LoggingTestBase for the logging base class and LoggingTestBaseExampleTest for a simple example that's using it.

Every contained test method is executed three times:

1. It's executed using the logging as defined in logback-test.xml as usual. This is supposed to help while writing/debugging the tests.

2. It's executed with all logging enabled and written to a file. This file is deleted after the test.

3. It's executed with all logging disabled.

Yes, LoggingTestBase needs documentation ;)

み格子的夏天 2024-07-30 06:36:14

您是否尝试过简单地维护两个单独的日志配置文件? 每个记录器都会从根记录器开始在不同级别进行记录。

禁用所有日志记录

...
<root>
    <priority value="OFF"/>
    <appender-ref ref="LOCAL_CONSOLE"/>
</root>
...

启用所有日志记录

...
<root>
    <priority value="ALL"/>
    <appender-ref ref="LOCAL_CONSOLE"/>
</root>
...

执行时将通过系统参数在类路径上指定不同的配置:

-Dlog4j.configuration=path/to/logging-off.xml
-Dlog4j.configuration=path/to/logging-on.xml

Have you tried simply maintaining two separate log configuration files? Each one would log at different levels from the root logger.

All logging disabled:

...
<root>
    <priority value="OFF"/>
    <appender-ref ref="LOCAL_CONSOLE"/>
</root>
...

All logging enabled:

...
<root>
    <priority value="ALL"/>
    <appender-ref ref="LOCAL_CONSOLE"/>
</root>
...

Execution would specify different configurations on the classpath via a system parameter:

-Dlog4j.configuration=path/to/logging-off.xml
-Dlog4j.configuration=path/to/logging-on.xml
小猫一只 2024-07-30 06:36:14

我建议从 JUnit 切换到 TestNG。 TestNG 比 JUnit 具有许多高级功能。 它使您能够使用不同的配置多次运行测试,我想这就是您所需要的

I would recommend switching from JUnit to TestNG. TestNG has a lot of advanced features over JUnit. It enables you to run your tests multiple times with different configuration and I guess that's what you need

岁月静好 2024-07-30 06:36:14

eqbridges 建议使用不同的日志记录上下文简单地运行两次测试似乎是最简单的。 您不必记住在每个受祝福的测试中编写逻辑,这是一个很大的优势。 另一个是您可以很容易地看到哪个日志记录级别是罪魁祸首。

话虽这么说,如果您只需在一次测试运行中执行此操作,则有几种策略。

对于 3.8,我会将所有内容放入套件中,并制作两个套件,每个套件对应一个日志记录级别,这会在运行测试之前设置日志记录级别。 从功能上讲,这与使用不同的命令行参数运行整个测试套件两次是一样的,只不过您只需运行一次即可获得它。

在 JUnit 4.x 中,我想到了几个附加选项:

一个是自定义运行器。 虽然我无法立即想到要完成这项工作所需要做的所有事情,但实际运行测试两次并使用自定义运行器 @RunWith 注释测试的运行器可以工作。

另一种是参数化测试。 尽管您实际上必须设置每个测试来接受参数(这需要一个接受参数的构造函数),然后根据参数设置日志级别。

编辑:为了响应您对参数化测试操作方法的请求,此处 是运行器上的 javadoc,可帮助您入门,这里是一个更实用的指南。

eqbridges suggestion of simply running the tests twice with different logging contexts seems the simplest. You don't have to remember to code the logic in every blessed test, for one big advantage. The other is that you can see which logging level is to blame very easily.

That being said, there a couple of strategies if you just had to do this in one test run.

For 3.8 I would put everything in suites, and make two suites, one for each logging level, which sets the logging level before running the tests. This is functionally the same thing as running the whole test suite twice with different command line parameters, except that you get it with one run.

In JUnit 4.x a couple of additional options come to mind:

One is a custom runner. Although I can't think off hand of everything you would have to do to make this work, but a runner that actually runs the test twice and annotating the test with @RunWith your custom runner could work.

The other is parameterized tests. Although you would actually have to set up every test to take parameters (this requires a constructor which takes the arguments) and then set the log level according to the parameter.

EDIT: In response to your request for a how-to on the paramterized tests, here is the javadoc on the runner to get you started, and here is a more practical guide.

云醉月微眠 2024-07-30 06:36:14

如果您觉得打开所有功能后日志记录过多,也许您可​​以尝试减少日志记录量。 如果计算机无法生成太多内容,那么它就不是很有用,更不用说人类阅读了。

If you feel you have too much logging if you turn everything on, perhaps you could try to cut down the amount of logging. Its not very useful if it too much for the computer to procude never mind a human to read.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文