在Kotlin中进行测试无法访问受保护的方法

时间:2017-01-15 16:04:44

标签: unit-testing kotlin

我想测试B类:

class B : A {
    override fun init() {
        // do work here
    }
}

class A {
    protected fun init() { } // will be called by internal logic
}

并且在Java中调用时没有问题:b.init()在测试方法中(测试类与测试主题在同一个包中),但在Kotlin编译器中抱怨:

  

无法访问' init'它在' B'

中受到保护
@Test
fun `checks init`() {
    val b = B()
    b.init()
    // assert work done
}

为什么它不起作用?这怎么可以解决(我想避免公开方法)?

3 个答案:

答案 0 :(得分:5)

由于Kotlin通过不允许包访问来降低protected(与Java相比)的可见性,我可以找到的最佳选择是使用反射进行解决(因为这是用于测试我没有理由不这样做)< / p>

private fun invokeHiddenMethod(name: String) {
    val method = sut.javaClass.getDeclaredMethod(name)
    method.isAccessible = true
    method.invoke(testSubject)
}

答案 1 :(得分:4)

Java中的

protected与Kotlin中的不一样。

在Java中,同一个包中的所有内容都可以访问protected方法。 见In Java, difference between default, public, protected, and private

在Kotlin中,protected表示您只能在同一个类或其任何子类中访问它。见Visibility Modifiers - Kotlin

唯一可行的方法是使用internal修饰符,并使该方法对同一模块中的测试可见。

答案 2 :(得分:-1)

@Test
fun getTotalTest() {
//test private method: getTotal(int, int)

//sample class
val student = StudentKt("Leon", 80, 60)

//set your expected value
val expected = 80 + 60
var actual = 0

// bring the type of the parameters
//getDeclaredMethod("method name", parameter type, ...)
//if there is no parameter = getDeclaredMethod("method name")
val  method = student.javaClass.getDeclaredMethod(
     "getTotalScore", Int::class.java, Int::class.java
)

//set it could be Accessible
method.isAccessible = true

//set your parameters
val parameters = arrayOfNulls<Any>(2)
parameters[0] = 80
parameters[1] = 60

//force the result as Int(the output type of your method)
actual = method.invoke(student, *parameters) as Int
//or you could just put the parameters in the method
//actual = method.invoke(student, 80, 60) as Int

//check your result
assertEquals(expected, actual)
}

-------------------------------------------- -----------------------------------

我写了一个样本供您参考

kotlin是用Java开发的。

所以也许您应该首先知道如何用Java编写测试用例

  

Java示例类

public class Student {

//properties
private String id = "A01234567";
public String name = "Leon";
private int englishScore = 0;
private int mathScore = 0;

//constructor
public Student(String name, int englishScore, int mathScore) {
    this.name = name;
    this.englishScore = englishScore;
    this.mathScore = mathScore;
}

//private method
private boolean isPass() {
    return  (englishScore>=60 && mathScore>=60);
}

//private method
private int getTotalScore(int english, int math) {
    return  english+math;
}

//public method
public double getAvg() {
    return (englishScore+mathScore) / 2.0;
}
}
  

Java测试样本

import org.junit.Assert;
import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static org.junit.Assert.assertEquals;

public class StudentTest {


@Test
public void idTest() {
    //test private property: id
    Student student = new Student("Leon", 80, 60);

    String expected = "A01234567";
    String actual = "";

    Field field = student.getClass().getDeclaredField("id");
    field.setAccessible(true);
    actual = (String) field.get(student);

    assertEquals(expected, actual);
}

@Test
public void nameTest() {
    //test public property: name
    Student student = new Student("Leon", 80, 60);

    String expected = "Leon";
    String actual =  student.name;

    assertEquals(expected, actual);
}

@Test
public void isPassTest() {
    //test private method: isPass() -- no parameter
    Student student = new Student("Leon", 80, 60);

    boolean expected = true;
    boolean actual = false;

    Method method = student.getClass().getDeclaredMethod("isPass");
    method.setAccessible(true);
    actual = (boolean) method.invoke(student);

    assertEquals(expected, actual);
}

@Test
public void getTotalTest() {
    //test private method: getTotal()
    Student student = new Student("Leon", 80, 60);

    int expected = 80+60;
    int actual = 0;

    // bring the type of the parameters
    Method method = student.getClass().getDeclaredMethod("getTotalScore", int.class, int.class);
    method.setAccessible(true);

    //set parameters
    Object[] parameters = new Object[2];
    parameters[0] = 80;
    parameters[1] = 60;

    actual = (int) method.invoke(student, parameters);

    assertEquals(expected, actual);
}

@Test
public void getAvgTest() {
    //test public method: getAvg()
    Student student = new Student("Leon", 80, 60);

    double expected = (80+60)/2.0;
    double actual = student.getAvg();

    //(double expected, double actual, double epsilon)
    assertEquals(expected, actual ,0);
}
}

-------------------------------------------- -----------------------------------

科林版本

  

科特琳样品班

class StudentKt(name:String, englishScore:Int, mathScore:Int) {

//properties
private var id:String = "A01234567"
var name:String = "Leon"
private var englishScore:Int = 0
var mathScore:Int = 0

//initialization
init {
    this.name = name
    this.englishScore = englishScore
    this.mathScore = mathScore
}

//private method
private fun isPass():Boolean {

    return (englishScore>=60 && mathScore>=60)
}

//private method
private fun getTotalScore(english: Int, math: Int): Int {
    return english + math
}

//public method
fun getAvg():Double {
    return (englishScore + mathScore) / 2.0
}

}
  

科特林样品测试

import junit.framework.Assert.assertEquals
import org.junit.Test
import java.lang.reflect.InvocationTargetException

class StudentKtTest {

@Test
fun idTest() {
    //test private property: id
    val student = StudentKt("Leon", 80, 60)

    val expected = "A01234567"
    var actual = ""

    val field = student.javaClass.getDeclaredField("id")
    field.isAccessible = true
    actual = field.get(student) as String

    assertEquals(expected, actual)
}

@Test
fun nameTest() {
    //test public property: name
    val student = StudentKt("Leon", 80, 60)

    val expected = "Leon"
    val actual = student.name

    assertEquals(expected, actual)
}

@Test
fun isPassTest() {
    //test private method: isPass() -- no parameter
    val student = StudentKt("Leon", 80, 60)

    val expected = true
    var actual = false

    //you don't need set type if there is no parameter in the method
    val method = student.javaClass.getDeclaredMethod("isPass")
    method.isAccessible = true
    //you don't need set type if there is no parameter in the method
    actual = method.invoke(student) as Boolean

    assertEquals(expected, actual)
}

@Test
fun getTotalTest() {
    //test private method: getTotal()
    val student = StudentKt("Leon", 80, 60)

    val expected = 80 + 60
    var actual = 0

    // bring the type of the parameters
    val  method = student.javaClass.getDeclaredMethod(
         "getTotalScore", Int::class.java, Int::class.java
    )

    method.isAccessible = true

    //set parameters
    val parameters = arrayOfNulls<Any>(2)
    parameters[0] = 80
    parameters[1] = 60

    actual = method.invoke(student, *parameters) as Int

    assertEquals(expected, actual)
}

@Test
fun getAvgTest() {
    //test public method: getAvg()
    val student = Student("Leon", 80, 60)

    val expected = (80 + 60) / 2.0
    val actual = student.avg

    //(double expected, double actual, double epsilon)
    assertEquals(expected, actual, 0.0)
}
}