The following unit test will pass repeatedly in both your IDE (ex: run
until failure) or in stressNewTest:
public class SystemOutRuleTest {
@Rule
public SystemOutRule systemOutRule = new SystemOutRule().enableLog();
@Test
public void passesRepeatedly() {
System.out.println("hello");
assertThat(systemOutRule.getLog()).contains("hello");
}
}
But the following test will fail after the first time the test is executed
(ex: run until failure in IntelliJ) :
public class SystemOutRuleTest {
@Rule
public SystemOutRule systemOutRule = new SystemOutRule().enableLog();
@Test
public void passesFirstTime() {
LogManager.getLogger().info("hello");
assertThat(systemOutRule.getLog()).contains("hello");
}
}
If you need to verify that a class is logging a specific message in some
circumstances then you can extract that test from the Unit Test and move it
to an Integration Test. You do *not* have to annotate SystemOutRule
with @ClassRule but I recommend doing that especially if you're using other
Rules doing some complicated setUp involving Geode classes:
public class CacheLoggingIntegrationTest {
private InternalCache cache;
@Rule
public SystemOutRule systemOutRule = new SystemOutRule().enableLog();
@Before
public void setUp() {
cache = (InternalCache) new CacheFactory().set(LOCATORS, "").create();
}
@After
public void tearDown() {
cache.close();
}
@Test
public void passesFirstTime() {
cache.getLogger().info("hello");
assertThat(systemOutRule.getLog()).contains("hello");
}
}
If you change the SystemOutRule in CacheLoggingIntegrationTest to a
static @ClassRule:
@ClassRule
public static SystemOutRule systemOutRule = new
SystemOutRule().enableLog();
...then it will repeatedly pass if you run-until-failure in IntelliJ but
only because IJ ends up running it as one test class. It will still fail
when run repeatedly by stressNewTest or if a previous test class caused
Log4J2 to startup within the same JVM.
On Mon, Nov 26, 2018 at 1:46 PM Kirk Lund <[email protected]> wrote:
> Log4J and Logback both capture a reference to System.out and/or System.err
> and then use that reference thereafter. This means that any manipulation of
> System.out or System.err -- using System.setOut(PrintStream) or
> System.setErr(PrintStream) -- probably isn't going to play nicely with
> logging in general.
>
> Here's what that means for Geode tests...
>
> If you're writing a Unit Test, then that test will be executed in a
> long-lived JVM in which other Unit Tests are also going to be executed. The
> SystemOutRule from system-rules will work with code that uses System.out
> directly but will not work repeatedly with loggers.
>
> If you're writing an Integration Test or Distributed Test, then that test
> will be executed in a fresh JVM (fork every one is the config), so as long
> as you can get the SystemOutRule before to invoke before logging, the test
> will behave. In general that means you'll want to annotate it
> with @BeforeClass.
>
> Here are a couple examples in which log4j2 captures System.out to
> illustrate why SystemOutRule can't intercept the output after invoking
> System.setOut(PrintStream):
>
> log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java:43:
> private static final PrintStream DEFAULT_STREAM = System.out;
>
> log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java:87:
> ps = System.out;
>
> If SystemOutRule executes its before -- System.setOut(PrintStream) --
> after the above code, logging will be printing to a different PrintStream
> than the PrintStream that SystemOutRule inserted into System.
>