代码在Scala中,但问题也与其他语言有关。
假设我们有一些课程:
class SomeClass(inputString: String) {
private val name: String = readName()
private val method: Method = readMethod()
def sayHello(): String = {
if (method == USE_XXX) {
s"Hello, my name is $name"
} else "I don't know"
}
private def readName(): String = {
val nameRegex ="""(?mi)^NAME\s*:\s*(.*)$""".r
val name = for (m <- nameRegex.findFirstMatchIn(inputString)) yield m.group(1)
name.getOrElse(throw new CustomException("No name in input"))
}
private def readMethod(): Method = {
val methodRegex = """(?mi)^METHOD\s*:\s*(.*)$""".r
val method = for (m <- methodRegex.findFirstMatchIn(inputString)) yield m.group(1)
method.getOrElse("") match {
case "XXX" => USE_XXX
case _ => UNKNOWN
}
}
sealed trait Method
case object USE_XXX extends Method
case object UNKOWN extends Method
}
如您所见,它的公共接口只是方法sayHello()
。
但是,当实例化该类的对象时,readName可能会引发异常,因为需要name
字段。
问题是,如何对此类进行单元测试。我想编写单元测试,以针对给定功能(隔离)的特定输入测试两个私有功能,例如:
tests for readName() with texts:
1) "NAME: ok"
2) "NAME: first occurance\nNAME: second occurance"
3) "name: keyword lowercase"
4) "NAME : whitespaces around semicolon"
5) AssertThrows[CustomException](SomeClass("NONAME"))
test for readMethod() with texts:
1) "NO METHOD KEYWORD" -> should have be UNKOWN
2) "METHOD: XXX"
3) "method: xxx"
etc.
问题在于,用于测试readMethod()的输入文本还应包含NAME:关键字,以防止引发异常。如果在每个测试中还存在其他字段和验证规则,则必须使用该字段和输入来满足所有验证规则,但要满足所有验证规则,因此很难维护测试服。
如何处理这样的问题?
最简单的方法是将异常抛出移至sayHello()
方法,但这有一个缺点-在系统的其他部分执行了一些昂贵的计算后,可能会调用该方法,但是它应该警告用户尽早(“尊敬的用户,您提供的数据有误,名称丢失。您真的要继续吗?”-或类似的东西)。
答案 0 :(得分:0)
此类的外部接口是构造函数(new SomeClass(string)
)和sayHello
方法。这是在任何单元测试中都应该测试的行为,并且这样做非常简单。
readName
和readMethod
是内部实现功能,可以用其他方式实现(例如(name, method) = readNameAndMethod()
)。因此,测试readName
或readMethod
是没有意义的,因为它们是实现的一部分。
如果要将readName
和readMethod
作为单独的功能进行测试,则可以将它们作为公共方法放在单独的可测试对象上。然后它们是一个公共接口,并且可以拥有自己的单元测试套件。