Dynamiczna analiza kodu dla SBT - testy jednostkowe

7 minutes to read

Wprowadzenie

Dynamiczna analiza programu to analiza oprogramowania komputerowego wykonywanego przez wykonywanie programów na rzeczywistym lub wirtualnym procesorze. Korzystanie z metryk testów, takich jak pokrycie kodu, zapewnia, że przetestowano odpowiednią ilość możliwych zachować programu. Aby analiza dynamiczna programu była skuteczna, program docelowy musi być wykonany z wystarczającą ilością danych wejściowych do testów, aby uzyskać interesujące zachowanie. Testy jednostkowe, testy integracyjne, testy systemowe i testy akceptacyjne wykorzystują dynamiczną analizę programu.

Za wikipedią.

W tym poście skupię się tylko na frameworkach do testów, testach jednostkowych i mierzeniu pokrycia kodu testami.

Frameworki dla testów

Test jednostkowy w uTest

W build.sbt dodajemy uTest do zależności projektu:

  libraryDependencies += "com.lihaoyi" %%% "utest" % "0.6.5" % "test"

i ustawiamy jako framework testowy:

  testFrameworks += new TestFramework("utest.runner.Framework"),

Tworzymy klasę Calculator, którą będziemy testować :

package pl.writeonly.re.shared

class Calculator {
  type T = Int

  def add(a: T, b: T): T = a * b

  def mul(a: T, b: T): T = a + b

  def leq(a: T, b: T): Boolean = a < b

}

Oraz testy dla niej:

package pl.writeonly.re.shared

import utest._

object CalculatorTest extends TestSuite {
  override val tests: Tests = Tests {
    val calculator = new Calculator()
    'addition - {
      val addition: (Int, Int) => Int = (x, y) => calculator.add(x, y)
      "0 + 0 == 0" - {
        assert(addition(0, 0) == 0)
      }
      "2 + 2 == 4" - {
        assert(addition(2, 2) == 4)
      }
    }
    'multiplication - {
      val multiplication: (Int, Int) => Int = (x, y) => calculator.mul(x, y)
      "0 + 0 == 0" - {
        assert(multiplication(0, 0) == 0)
      }
      "2 + 2 == 4" - {
        assert(multiplication(2, 2) == 4)
      }
    }
    'less_or_equal - {
      val less_or_equal: (Int, Int) => Boolean = (x, y) => calculator.leq(x, y)
      "0 <= 2 == true" - {
        assert(less_or_equal(0, 2))
      }
      "2 <= 0 == false" - {
        assert(!less_or_equal(2, 0))
      }
    }
  }
}

Wszystko kompilujemy i uruchamiamy testy za pomocą polecenia:

sbt clean compile test

Testy przeszły, jesteśmy szczęśliwi:

[info] -------------------------------- Running Tests --------------------------------
[info] + pl.writeonly.re.shared.CalculatorTest.addition.0 + 0 == 0 0ms  
[info] + pl.writeonly.re.shared.CalculatorTest.addition.2 + 2 == 4 0ms  
[info] + pl.writeonly.re.shared.CalculatorTest.multiplication.0 + 0 == 0 0ms  
[info] + pl.writeonly.re.shared.CalculatorTest.multiplication.2 + 2 == 4 0ms  
[info] + pl.writeonly.re.shared.CalculatorTest.less_or_equal.0 <= 2 == true 0ms  
[info] + pl.writeonly.re.shared.CalculatorTest.less_or_equal.2 <= 0 == false 0ms  
[info] Tests: 6, Passed: 6, Failed: 0

Testowanie testów - mierzenie pokrycia kodu testami

Skąd mamy mieć pewność, że przetestowaliśmy klasę Calculator w wystarczający sposób? Możemy to częściowo sprawdzić mierząc pokrycie kodu produkcyjnego (tj. klasy Calculator) testami.

Jeśli chodzi o narzędzia do mierzenia pokrycia kodu testami to tutaj król jest jeden i jest nim scoverage. Posiada on wtyczki do:

Przy czym użyjemy tutaj tylko pierwszej z nich.

Dodajemy sbt-scoverage do build.sbt:

addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1")

I wykonujemy:

sbt clean coverage test && sbt coverageReport

gdzie:

Niestety powyższe polecenie działa tylko dla implementacji Scala/JVM i Scala.js. Scala Native nie wspiera instrumentacji kodu. Dlatego projekt resentiment trzeba kompilować za pomocą polecenia:

sbt clean compile re/test coverage reJVM/test reJS/test && sbt coverageReport

Teraz możemy otworzyć pliki <folder_projektu>/re/js/target/scala-2.11/scoverage-report/index.html oraz <folder_projektu>/re/jvm/target/scala-2.11/scoverage-report/index.html i zobaczyć, że klasa Calculator ma 100% pokrycia kodu testami. Lider nietechniczny i Product Owner powinni być z nas zadowoleni.

Share on:
Follow