我是单元测试的新手,最近尝试了JUnit测试和Mockito。
我正在尝试对调用多个私有方法并创建其他类的私有对象的方法进行单元测试。
如何对方法进行单元测试。
例如,如果我有以下代码:
class ClassToTest {
private OuterClass outerClass;
public Boolean function() {
outerClass = new OuterClass(20);
return innerFunction(outerClass.getValue());
}
private Boolean innerFunction(int val) {
if (val % 2 == 0)
return true;
return false;
}
}
我很困惑如何测试公共功能。
答案 0 :(得分:2)
方法的实现方式无关紧要;该方法应遵循的合同,并且 是您正在通过测试验证的内容。在此示例中,如果function()
的值为偶数,则true
应该返回outerClass
。完成此操作的一种方法是注入ClassToTest
实例(传递到outerClass
构造函数中),以便在测试时可以控制该值:
@Test
public void trueWhenEven() {
var outer = new OuterClass(2);
var ctt = new ClassToTest(outer);
assertTrue(ctt.function());
}
有时候契约只是一个方法调用其他一些对象上的方法。在这种情况下,您可以使用Mockito或类似的库来验证交互。
答案 1 :(得分:1)
您将直接通过一些不重要的问题开始单元测试。
问题1:如何处理实现细节/私有函数?答案:单元测试是关于发现代码中的错误,这是单元测试(以及其他大多数目标)的主要目标之一。各种测试)。另一个主要目标是通过在软件更改时充当回归测试来防止引入错误。错误存在于实现中-不同的实现带有不同的错误。因此,请务必测试实现细节。覆盖率分析是这里支持的一个重要工具,它可以向您显示测试已达到实现代码的哪些部分。
您甚至可以测试功能约定以外的方面:a)负面测试是有意检查无效/未指定输入行为的测试,对于确保系统安全至关重要。因为,即使提供了无效的输入,系统也不应因为例如读取或写入越界内存而被黑客入侵。但是,这可能不适用于您的示例,因为您的方法最有可能被指定为实现“总计函数”而不是“部分函数”。 b)甚至可以执行超出当前实现所需的测试(如果可以访问)的实现细节。这样做可以防止在即将对组件进行更改时出现错误,例如API扩展。
但是,还有单元测试的次要目标。其中之一是避免在实现细节更改时不必要地破坏测试。达到次要目标的一种方法是通过公共API测试实现细节。这样,对实现细节的某些重新设计不会破坏您的测试:重命名,拆分或合并私有功能不会影响测试。但是,切换到其他算法可能会需要您重新考虑测试:对于fibonacci函数的迭代/递归实现的测试与使用Moivre / Binet的闭合形式表达式的实现看上去不同,或者查找表的实现。
对于您的示例,这意味着您应该尝试通过公共API测试私有功能的功能。
问题2:如何处理对软件其他部分的依赖性?单元测试的重点是在孤立的小型代码段中查找错误。当这些代码段与其他代码段具有依赖性时,这可能会对您正确进行单元测试的能力产生负面影响。但是,是否确实如此取决于实际的依赖性。例如,如果您的代码使用Math.sin()
函数,那么这也是对其他代码部分的依赖,但是这种依赖通常不会损害您正确测试代码的能力。
在以下情况下,对其他组件的依赖性使您感到困扰:使用其他组件使得很难激发被测代码中所有有趣的场景。或者,使用其他组件会导致不确定的行为(时间,随机性,...)。或者,使用其他组件会导致构建或执行时间过长。或者,其他组件有故障或什至不可用。
如果不满足所有这些条件(通常使用Math.sin()
函数),则通常可以将其他组件作为测试的一部分。但是,您应该记住,在单元测试中,您仍将重点放在代码中的错误上,并且不要开始编写实际测试其他组件的测试:请记住,其他组件具有自己的测试。
在您的示例中,您选择了Outerclass
具有某些看似微不足道的功能。在这种情况下,您可以只在其余测试中使用Outerclass。但是,从您的角度来看,这只是一个例子-根据上述标准,真正的其他课堂实际上可能会令人不安。如果真是这样,那么您就必须以某种方式来管理这种依赖关系,而这一切都需要以某种方式实现易于测试的设计。
这里有一整套方法,因此您最好在网上搜索“可测试性设计”和“控制反转”。并且,您还应该尝试了解单元测试和集成测试的区别:这将帮助您避免尝试将单元测试应用于应该通过集成测试进行测试的代码部分。
答案 2 :(得分:0)
通常,对于Mockito,这将需要使用依赖注入,然后您将为测试注入OuterClass的模拟。
如果您真的想在不添加Spring类型框架的情况下进行测试,我可以考虑3个选择:
1)进行集成测试并测试所有内容的真实实例
2)修改您的代码,以便通过设置器或构造函数的传入对象创建OuterClass,然后为测试传递模拟
3)将private OuterClass outerClass;
更改为protected OuterClass outerClass;
,并确保测试包结构与实际代码包结构相同,然后可以在测试设置中进行outerClass = Mockito.mock(OuterClass);
。