KotlinTest тестирование и генераторы на основе свойств - PullRequest
1 голос
/ 11 ноября 2019

У меня есть следующее определение ориентированного графа в Котлине. (Я все еще изучаю Kotlin, поэтому, пожалуйста, прости любые недостатки. Улучшения и предложения всегда приветствуются.) Моя цель - иметь метод reverse, который поддерживает вершины и петли, но меняет направления других ребер.

// We use an edge list because it makes it the easiest to swap.
data class ReversibleDirectedGraph<T>(val vertices: Set<T>, val edgeList: List<Pair<T,T>>) {

    // This should be a self-inverting function.
    fun reverse(): ReversibleDirectedGraph<T> {
        // Make sure all vertices in edgeList are in vertices.
        val allVertices = edgeList.flatMap { it.toList() }
        require(allVertices.all { it in vertices }) { "Illegal graph specification" }

        // Swap the edges.
        val newEdgeList = edgeList.map { it.second to it.first }
        return ReversibleDirectedGraph(allVertices.toSet(), newEdgeList)
    }
}

fun main() {
    // Example test: works correctly. Double edge reversal results in original graph.
    val g = ReversibleDirectedGraph(setOf(0, 1, 2, 3),
        listOf(0 to 1, 2 to 1, 3 to 2, 3 to 0, 1 to 3))
    println(g)
    val gr = g.reverse()
    println(gr)
    val grr = gr.reverse()
    println(grr)
    println(grr == g)
}

Я бы хотел использовать тестирование на основе свойств для тестирования этого кода с использованием KotinTest, но у меня возникают проблемы с его структурированием для правильного получения случайных выборок неориентированных графов. Если мне удастся достичь этой точки, я смогу дважды изменить направление края и затем убедиться, что достигнут исходный график.

Я знаком с Gen.list, Gen.choose и т. Д., Но не могуПохоже, что они соединяют кусочки вместе, чтобы получить конечный продукт, то есть случайный неискаженный график.

Я уже понял это, но это явно недостающие кусочки, и я надеялся, что кто-то сможет помочь,Я подозреваю, что мог бы сделать это в Scala, так как у меня там больше опыта, но я полон решимости изучать Kotlin. В конечном итоге, что-то вроде:

class ReversibleDirectedGraphTest: StringSpec() {
    init {
        "reversibleDirectedGraphTest" {
            forAll { g: ReversibleDirectedGraph<Int> ->
                assertEqual(g.reverse().reverse() == g) }
            }
        }
    }
}

Любая помощь / предложения будут с благодарностью. Спасибо!

1 Ответ

0 голосов
/ 12 ноября 2019

В итоге я последовал предложению @monkjack и создал свой собственный Gen. Я должен был явно указать Gen для forAll, и возникнет редкое исключение: «граница должна быть больше источника», но это работает, и подавляющее большинство создаваемых тестовых случаев являются действительными и недолжны быть перехвачены try...catch.

class GraphGen: Gen<ReversibleDirectedGraph<Int>> {
    override fun constants() =
        listOf(
            ReversibleDirectedGraph(emptySet(), emptySet()),
            ReversibleDirectedGraph(setOf(0), setOf(0 to 0)),
            ReversibleDirectedGraph(setOf(0, 1), emptySet()),
            ReversibleDirectedGraph(setOf(0, 1), setOf(0 to 1))
        )

    override fun random(): Sequence<ReversibleDirectedGraph<Int>> = generateSequence {
        val vertexGen = Gen.choose(0, 20)
        val vertices = Gen.set(vertexGen).random().first()
        val vertexList = vertices.toList()
        val edgeGen = Gen.set(Gen.pair(Gen.from(vertexList), Gen.from(vertexList))).random()

        // On rare occasions, this throws an exception with origin and bound, probably due
        // to the number of sets. In those cases, we use an emptySet instead as a placeholder.
        val edges = try { edgeGen.first() } catch (e: IllegalArgumentException) { null }
        ReversibleDirectedGraph(vertices, edges?: emptySet())
    }
}

class ReversibleDirectedGraphTest: StringSpec() {
    init {
        "reversibleDirectedGraphTest" {
            forAll(GraphGen()) { g -> g.reverse().reverse() == g }
        }
    }
}
...