Arnold
Arnold

Reputation: 83

Spock:Unit Test for Graphic2d and BuffredImage

I am having a difficulty how to test the below class.How Can I write a test which checks whether graphics draw "name" or not with specific width ,heigh ?And what other tests I can write against the following class using spock. And How Can I mock the exception?

 class NameCardCreator{
    
       private val bufferedImage = BufferedImage(580, 380, BufferedImage.TYPE_INT_ARGB)
        var name: String? = null
        const val DISCLAIMER = "Name is Correct"
        fun createNameCard(): String?
        {
            var generic64: String? = null
            try {
                dashed = BasicStroke(3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0f, floatArrayOf(5f), 0f)
                val graphics =  bufferedImage.createGraphics()
                graphics.drawRoundRect(20, 20, 535, 340, 15, 15) //actual card size
                val fontMetrics = graphics.fontMetrics
                graphics.drawLine(300, 120, 540, 120)
                if (!name.isNullOrEmpty()) {
                    graphics.drawString(name, 300, 110)
                }
                graphics.drawString(DISCLAIMER, 50, 290)
                generic64 = toBase64(bufferedImage)
                return generic64
            } catch (ie: IOException) {
                logger.info("Name card could not be generated $ie.message")
            }
            return generic64
        }
        
        protected fun toBase64(bufferedImage: BufferedImage): String? {
            var base64Str: String? = null
            var os: ByteArrayOutputStream? = null
            var b64: OutputStream? = null
            try {
                os = ByteArrayOutputStream()
                b64 = Base64OutputStream(os)
                ImageIO.write(bufferedImage, "png", b64)
                base64Str = os.toString("UTF-8")
            } catch (e: Exception) {
                logger.error(e.message)
            } finally {
                os?.close()
                b64?.close()
            }
            return base64Str
        }
    }

Upvotes: 0

Views: 247

Answers (1)

kriegaex
kriegaex

Reputation: 67457

I do not speak Kotlin, but I played around with your class a little bit and just added a separate constructor in which you can inject a BufferedImage. As soon as you can do that, you also can inject a mock. If you stub createGraphics() in that mock to return yet another Graphics2D mock, you can easily verify if graphics.drawString(name, 300, 110) was called with the name you expect.

Besides, there is no need to catch any IOException in createNameCard() because in toBase64() you catch all exceptions. So either you completely remove the try-catch in createNameCard() (variant A) or you remove the catch clause in toBase64(), giving you something to catch in the calling method (variant B).

Anyway, I am keeping both catch blocks and am showing you how to get 100% test coverage for the whole file including the error cases:

package de.scrum_master.testing.kotlin

import org.apache.commons.codec.binary.Base64OutputStream
import java.awt.BasicStroke
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.OutputStream
import javax.imageio.ImageIO

class NameCardCreator {
  private var bufferedImage = BufferedImage(580, 380, BufferedImage.TYPE_INT_ARGB)
  private var name: String? = null
  val DISCLAIMER = "Name is Correct"

  constructor()

  constructor(name: String?) {
    this.name = name
  }

  constructor(bufferedImage: BufferedImage, name: String?) {
    this.bufferedImage = bufferedImage
    this.name = name
  }

  fun createNameCard(): String? {
    var generic64: String? = null
    try {
      val dashed = BasicStroke(3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0f, floatArrayOf(5f), 0f)
      val graphics = bufferedImage.createGraphics()
      graphics.drawRoundRect(20, 20, 535, 340, 15, 15) // actual card size
      val fontMetrics = graphics.fontMetrics
      graphics.drawLine(300, 120, 540, 120)
      if (!name.isNullOrEmpty()) {
        graphics.drawString(name, 300, 110)
      }
      graphics.drawString(DISCLAIMER, 50, 290)
      generic64 = toBase64(bufferedImage)
      return generic64
    } catch (ie: IOException) {
      println("Name card could not be generated: $ie.message")
    }
    return generic64
  }

  protected fun toBase64(bufferedImage: BufferedImage): String? {
    var base64Str: String? = null
    var os: ByteArrayOutputStream? = null
    var b64: OutputStream? = null
    try {
      os = ByteArrayOutputStream()
      b64 = Base64OutputStream(os)
      ImageIO.write(bufferedImage, "png", b64)
      base64Str = os.toString("UTF-8")
    } catch (e: Exception) {
      println("Base64 encoding failed: $e.message")
    } finally {
      os?.close()
      b64?.close()
    }
    return base64Str
  }

}
package de.scrum_master.testing.kotlin

import org.apache.commons.codec.binary.Base64
import spock.lang.Specification

import javax.imageio.ImageIO
import java.awt.*
import java.awt.image.BufferedImage

class NameCardCreatorTest extends Specification {
  def "check that base64 actually encodes an image"() {
    given:
    def nameCardCreator = new NameCardCreator("John Doe")

    when:
    def imageBase64 = nameCardCreator.createNameCard()
//    println imageBase64
    def imageBytes = new Base64().decode(imageBase64)
//    new FileOutputStream("name-card.png").write(imageBytes)
    def bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes))

    then:
    bufferedImage.width == 580
    bufferedImage.height == 380
  }

  def "check that name was written on card"() {
    given:
    def graphics2D = Mock(Graphics2D)
    def bufferedImage = Mock(BufferedImage) {
      // Return Graphics2D mock
      createGraphics() >> graphics2D
    }
    when:
    new NameCardCreator(bufferedImage, "John Doe").createNameCard()

    then:
    1 * graphics2D.drawString("John Doe", _, _)
  }

  def "exception during card creation leads to null result"() {
    given:
    def bufferedImage = Mock(BufferedImage) {
      // Throw an exception for each method call
      _(*_) >> { throw new IOException("uh-oh") }
    }
    expect:
    new NameCardCreator(bufferedImage, "John Doe").createNameCard() == null
  }

  def "exception during base64 encoding leads to null result"() {
    given:
    def bufferedImage = Mock(BufferedImage) {
      // Throw an exception for each method call
      _(*_) >> { throw new IOException("uh-oh") }
    }

    expect:
    new NameCardCreator().toBase64(bufferedImage) == null
  }
}

Upvotes: 1

Related Questions