JustLikeThat JustLikeThat - 1 year ago 129
Scala Question

How to write unit test(s) for custom OutputStream

I've had to create an

class (inspired by this) that writes to a logger instead of
, so I came up with the following:

case class OutputStreamLogger(level: Level) extends OutputStream {
val logger: Logger = org.slf4j.LoggerFactory.getLogger("OutputStreamLogger")
private var mem = ""

* Writes byte to Logger, flushes automatically at EOL
* @param b Int Int representation of character to get written
* @return Unit
def write(b: Int): Unit = {
// To prevent internal conversion from byte to int
val bytes = new Array[Byte](1)
// Get least significant byte from int argument
bytes(0) = (b & 0xff).toByte
// Turn byte array into String
mem += new String(bytes)

// Automatically flush at EOL
if(mem.endsWith("\n")) {
// Grab everything but newline
mem = mem.substring(0, mem.length - 1)
// Log it

* Sends output bytes to logger at specified level
* @return Unit
override def flush(): Unit = {
level match {
// Passing the format then the String eliminates
// need to check if logging at that level is enabled
case Level.TRACE => logger.trace("{}", mem)
case Level.DEBUG => logger.debug("{}", mem)
case Level.INFO => logger.info("{}", mem)
case Level.WARN => logger.warn("{}", mem)
case Level.ERROR => logger.error("{}", mem)
// Clear out buffer
mem = ""

How would I unit test this? I've looked at other examples for unit testing
s, but they all use
where I would want to use my
and don't seem to be testing the

Answer Source

The main testability issue with your output stream is probably this declaration:

val  logger:  Logger  =  org.slf4j.LoggerFactory.getLogger("OutputStreamLogger")

You have nailed the logger down to a concrete instance, where the initialization of that instance can not be customized. You can either now use a slf4j logging configuration during unit testing that writes to a temporary file and read that back. Maybe there is also a custom appender available that writes to an in memory buffer.

The other possibility is to change that declaration and allow a logger specification upon instance creation. You can then use this to inject a mock logger.

Testing then would just be writing something to the output stream and see if a newline flushes the output to the logger.

BTW - your implementation looks very inefficient as it creates a new string for every byte written. And having a local variable and a member with the same name is also confusing.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download