1.概述
在本教程中,我们将分析各种用例以及具有非抽象方法的抽象类的单元测试的可能替代解决方案。
注意测试抽象课程几乎总是通过具体实施的公共API,所以不要应用以下技术,除非你确定你在做什么。
2. Maven依赖项
让我们从Maven依赖项开始:
<依赖项> org.junit.jupiter groupID> JUNIT-JUPITER-ENGINE ARTIFACTID> 5.1.0 version> test scope> 依赖项> <依赖项> org.mockito groupID> Mockito-Core ArtifactId> 2.8.9 version> test scope> 依赖项> <依赖项> org.powermock groupID> POWERMOCK-MODULE-JUNIT4 ARTIFACTID> 1.7.4 version> test scope> <排除> JUNIT GROUPID> JUNIT ARTIFACTID> 排除> 排除> 依赖项> <依赖项> org.powermock groupID> POWERMOCK-API-MOCKITO2 ARTIFACTID> <版本> 1.7.4 version> test scope> 依赖项>
您可以找到这些库的最新版本Maven Central.。
Junit5没有完全支持Powermock。还,PowerMock-Module-Junit4仅用于第5节中提出的一个示例。
3.独立非抽象方法
让我们考虑一下我们有一个带有公共非抽象方法的抽象类的情况:
公共抽象类抽象依赖性{公共摘录int abstractfunc();public string defaultimpl(){返回“default-1”;}}
我们想测试该方法defaultimpl(),我们有两种可能的解决方案 - 使用具体课程或使用Mockito。
3.1。使用混凝土级
创建一个延伸的具体类抽象依赖性类,并使用它来测试方法:
公共类ConcreteImpl扩展了抽象的依赖性{@override public int abstractfunc(){return 4;}}
@test public void给dononabstractmethod_whenconcreeteimpl_testcorRectbehaviour(){concreteimpl结论= new concreteimpl();字符串实际= scaleass.defaultimpl();assertequals(“默认-1”,实际);}
该解决方案的缺点是需要使用所有抽象方法的虚拟实现创建混凝土类。
3.2。使用Mockito.
或者,我们可以使用Mockito.要创建模拟:
@test public void gendnonabstractmethod_whenmockitomock_testcorrectbehaviour(){abstractwordedendent abscls = mockito.mockassertequals(“default-1”,abscls.defaultimpl());}
这里最重要的部分是调用方法时,模拟的编写方法使用mockito.calls_real_methods.。
4.从非抽象方法调用的抽象方法
在这种情况下,非抽象方法定义了全局执行流程,而抽象方法可以根据用例以不同的方式编写:
公开抽象类摘要archodcalling {公共抽象字符串abstractfunc();public string defaultimpl(){stringres = appthtramptfunc();return(res == null)?“默认”:( RES +“默认”);}}
要测试此代码,我们可以使用与之前相同的两种方法 - 要么创建一个具体的类或使用Mockito创建模拟:
@test public vivendefaultimpl_whenmockabstractfunc_thenexpectedbehaviour(){appristrationmethodcalling cls = mockito.mock(abstractmethodcalling.class);Mockito.When(cls.abstractfunc()).thenreturn(“摘要”);mockito.docallrealmethod()。(cls).defaultimpl();assertequals(“摘要默认”,cls.defaultimpl());}
在这里,这是AbstractFunc()与我们更喜欢测试的返回值是托盘的。这意味着当我们称之为非抽象方法时defaultimpl(),它将使用此存根。
5.具有测试阻塞的非抽象方法
在某些情况下,我们要测试的方法调用包含测试障碍的私有方法。
在测试目标方法之前,我们需要绕过妨碍测试方法:
公共抽象类Abstractprivatemethods {public abstract int abstractfunc();public string defaultimpl(){return getcurrentdatetime()+“default-1”;私有字符串getCurrentDateTem(){return localDateTime.Now()。ToString();}}
在这个例子中,defaultimpl()方法调用私有方法getCurrentDateTime()。此私有方法在运行时获取当前时间,应在我们的单元测试中避免。
现在,要模拟这种私有方法的标准行为,我们甚至无法使用Mockito.因为它无法控制私有方法。
相反,我们需要使用Powermock.(N此示例仅适用于JUnit 4,因为JUnit 5无法支持此依赖项):
@runwith(powermockrunner.class)@preparefortest(appressprivatemethods.class)public classtration astuctprivatemethodsunittest {@test public void whodmockprivatemethod_thenverifybehaviour(){approvistiveprivatemethods mockclass = powermockito.mock(abstractprivatemethods.class);powermockito.docallrealmethod()。当(模型).defaultimpl();字符串DateTime = localDateTime.Now()。ToString();powermockito.doreturn(DateTime)。当(模仿,“getCurrentDatetime”);字符串实际= mockclass.defaultimpl();assertequals(DateTime +“Default-1”,实际);}}
此示例中的重要比特:
- @runwith.将Powermock定义为测试的跑步者
- @preparefortest(类)告诉Powermock准备课程以供以后处理
有趣的是,我们要问Powermock.存根私有方法getCurrentDateTime()。Powermock将使用反射来找到它,因为它无法从外部访问。
所以那当我们打电话的时候defaultimpl(),将调用为私有方法创建的存根而不是实际方法。
6.访问实例字段的非抽象方法
抽象类可以使用类字段实现内部状态。字段的值可能对所测试的方法产生显着影响。
如果字段是公开的或受保护的,我们可以轻松地从测试方法访问它。
但如果它是私人的,我们必须使用PowerMockito.:
公共抽象类Abstractinstancefields {受保护的int count;私人布尔静止= false;公共摘录int abstractfunc();public string testfunc(){if(count> 5){return“overflow”;返回活跃?“补充道:”被封锁“;}}
在这里,这是testfunc()方法使用实例级别字段数数和积极的在它返回之前。
测试时testfunc(),我们可以改变价值数数通过访问使用的实例来实现字段Mockito。
另一方面,用私人测试行为积极的领域,我们再次必须使用PowerMockito.,及其白盒子班级:
@test公共空白whiperpowermockitoandactivefieldtrue_thencorrectbehaviour(){appristrationinstancess instclass = powermockito.mock(appristrationinstancefields.class);powermockito.docallrealmethod()。当(instclass).testfunc();Whitebox.SetInternalstate(instClass,“Active”,True);assertequals(“添加”,instclass.testfunc());}
我们正在创建存根类powermockito.mock(),我们正在使用白盒子类来控制对象的内部状态。
价值积极的字段更改为真的。
7.结论
在本教程中,我们已经看到了多个示例,涵盖了很多用例。我们可以在许多场景中使用抽象类,具体取决于所遵循的设计。
此外,为抽象类方法写入单元测试与普通类和方法一样重要。我们可以使用不同的技术或不同的测试支持库来测试它们中的每一个。
完整的源代码可用在github上。