Spek: Attempting to memoize System.out causes test to not run.

Created on 31 Jan 2019  路  5Comments  路  Source: spekframework/spek

I'm unsure of how to actually describe this, but if I attempt to memoize something like

val originalOut by memoized<PrintStream>()
...
val originalOut by memoize { System.out }

the test fails to run in intellij, this is what it looks like

image

bug

Most helpful comment

I was thinking something like this:

// top level fields
internal var sysout= System.out
internal var syserr = System.err

Instead of using print do sysout.print(...). Then on your tests just override the value of sysout and syserr (probably in setupStreams).

sysout = PrintStream(...)
syserr = PrintStream(...)

Relying on System.out and System.err makes your test very brittle, as anyone can override the value at any given time. Also, a rouge print call from another thread can cause your tests to fail.

All 5 comments

Very strange indeed.

However it doesn't seam to happen in memoize but when System.setOut is called.

Here is a minimal reproducer:

object MemoizedStreamSpec : Spek({
  describe("System.setOut") {
    it("should replace standard output stream") {
      val baos = ByteArrayOutputStream()

      System.setOut(PrintStream(baos))

      print("Hello world")

      assertEquals("Hello world", String(baos.toByteArray()))
    }
  }
})

EDIT: It is only a problem of the IntellIJ plugin. Running the test from gradle pass as expected.

@jcornaz it doesn't work for you without the memoization? That's weird because for me if I leave it as

fun Root.setupStreams() {
    val outContent by memoized<ByteArrayOutputStream>()
    val errContent by memoized<ByteArrayOutputStream>()
    val originalOut = System.out
    val originalErr = System.err

    // Create new byte stream for each test
    beforeEachTest {
        System.setOut(PrintStream(outContent))
        System.setErr(PrintStream(errContent))
    }
    afterEachTest {
        System.setOut(originalOut)
        System.setErr(originalErr)
    }
}

it works fine.

I may know what the problem is. When tests are run within IJ a new processed is spawned with stdout and stderr captured; this is used to communicate the test run events to IJ. So my guess is since you are overriding the default print streams - IJ is not receiving any events. Is there a way for you not to write directly to System.out and System.err? Maybe some variables which default to System.out and System.err - but you override them on the tests.

Hmm. well the whole point of my project is to print stuff to console. I'm not sure of another way to test it 馃槩. I guess I could rewrite my function to not use println but instead use a writer and write straight to PrintStream and then mock that in some way, though I tried mocking PrintStream once and it didn't work.

Anyway, if this isn't actually related to spek then we can close it. Sorry for the bother.

I was thinking something like this:

// top level fields
internal var sysout= System.out
internal var syserr = System.err

Instead of using print do sysout.print(...). Then on your tests just override the value of sysout and syserr (probably in setupStreams).

sysout = PrintStream(...)
syserr = PrintStream(...)

Relying on System.out and System.err makes your test very brittle, as anyone can override the value at any given time. Also, a rouge print call from another thread can cause your tests to fail.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jcornaz picture jcornaz  路  4Comments

ddubson picture ddubson  路  7Comments

SalomonBrys picture SalomonBrys  路  8Comments

volkdown picture volkdown  路  6Comments

ruigoncalo picture ruigoncalo  路  7Comments