在前两篇 文章中,我主要讨论了使用测试进行开发的理念和态度。 在这篇文章中,我提供了一些提示和工具示例进行测试。
工具类
JUnit的
http://junit.org/
还有TestNG,这是个很棒的工具。 但是我对JUnit有更多的经验,因此我将描述这个框架。
- 使用最新版本。
- 了解您的测试工具!
- @RunWith
这是类注释。 它告诉JUnit使用不同的Runner运行(mockito和Spring运行器是我使用的最常见的运行器)import org.mockito.runners.MockitoJUnitRunner; ... @RunWith(MockitoJUnitRunner.class) public class MyClassTest { ... }
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "/META-INF/app-context.xml","classpath:anotherContext.xml" }) public class MyClassTest { ... } // You can inherit AbstractJUnit4SpringContextTests instead of using runner
- @规则
一种AOP。 最常见的即用型规则是TemporaryFolder规则。 它使您可以使用文件系统而不必担心打开和关闭文件。 规则示例可在此处找到。 - 参数化流道
真的很酷的工具。 它使您可以使用不同的输入和不同的预期输出运行相同的测试。 它可能会被滥用,并使其无法读取。 - 测试数据准备和维护技巧
Hamcrest
http://hamcrest.org/JavaHamcrest/
该库是JUnit的“扩展”。 没有它,我无法工作! Hamcrest库为我们提供了开箱即用的匹配器。 匹配器与assertThat(...,Matcher)
风格一起使用。 我几乎总是用这种味道。 (在上一篇文章中,有人建议我不应该使用assertTrue(…),而应该使用assertThat。) 匹配器类型很多:您可以忽略集合来验证集合中的现有对象。 您可以检查大于。 使用assertThat +匹配器可以使测试更具可读性。
assertThat(mapAsCache.containsKey(new CacheKey("valA", "valB")), is(true));
assertThat(cachPairs.size(), is(2));
assertThat(enity.getSomething(), nullValue(Double.class));
assertThat(event.getType(), equalTo(Type.SHOWN));
assertThat(bits, containsInAnyOrder(longsFromUsIndexOne, longsFromUsIndexZero));
您可以创建自己的Matcher。 很简单 这是一个验证正则表达式的匹配器示例。 https://github.com/eyalgo/junit-additions
模仿
https://code.google.com/p/mockito/
这是我不能没有的第二个图书馆。 它使您可以模拟被测类的依赖关系。 使用mockito可以模拟依赖项。 然后,您“告诉”模拟对象在某些输入中的行为。 您告诉它如果输入了一些输入将返回什么。 您可以验证被调用方法的输入参数。 您可以验证是否调用了某个方法(一次,从不,3次,等等)。 您可以检查方法/模拟调用的顺序。
看一下这个:
package eyalgo;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
//The RunWith automatically instantiate fields with @Mock annotation
//and injects to the tested class @InjectMocks
@RunWith(MockitoJUnitRunner.class)
public class NameConnectorTest {
@Mock
private NameConvention nameConventionAsMockField;
@InjectMocks
private NameConnector connector;
private NameConvention nameConventionAsMockOther;
@Before
public void setup() {
//This is another way to inject mocks (instead of the annotations above)
MockitoAnnotations.initMocks(this);
nameConventionAsMockOther = mock(NameConvention.class);
NameConnector otherConnector = new NameConnector(nameConventionAsMockOther);
}
@Test
public void showSomeMockitoExamples() {
NameConvention nameConventionAsMock = mock(NameConvention.class, "Name for this mock");
// Stub and tell your mock to do something
when(nameConventionAsMock.bigBangConvention("INPUT")).thenReturn("Some output");
// Throw exception for some input
when(nameConventionAsMock.bigBangConvention("Other INPUT")).thenThrow(new RuntimeException("oops"));
// Do more complicated stuff in the "when"
Answer answer = new Answer() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
//do something really complicated
return "some output";
}
};
//Show also hamcrest matchers
when(nameConventionAsMock.bigBangConvention(argThat(equalTo("my name is Inigo Montoya")))).then(answer);
// Run the test..
//Verify some calls
verify(nameConventionAsMock).bigBangConvention("INPUT");
verify(nameConventionAsMock, times(10)).bigBangConvention("wow");
// Verify that the method was never called. With any input
verify(nameConventionAsMock, never()).bigBangConvention(anyString());
verifyNoMoreInteractions(nameConventionAsMock);
verifyZeroInteractions(nameConventionAsMockField);
//Check order of calls
InOrder order = inOrder(nameConventionAsMock, nameConventionAsMockOther);
order.verify(nameConventionAsMock).bigBangConvention("INPUT");
order.verify(nameConventionAsMock).bigBangConvention("other INPUT");
}
}
其他模拟工具
- PowerMock和EasyMock
当使用遗留代码时,这两个非常有用。 它们使您可以测试私有方法,静态方法以及通常无法执行的其他操作。 我认为,如果您需要它们,则设计有问题。 但是,有时您将外部库与单例和/或静态方法一起使用。 有时您处理的旧代码不太适合测试。 在这类情况下,这些模拟库可以帮助https://code.google.com/p/powermock/ http://easymock.org/ - JMockit http://jmockit.github.io/
- jMock http://jmock.org/
杰贝夫
http://jbehave.org/
JUnit,mockito,hamcrest用于单元测试 。 JBehave不完全相同。 它是用于行为驱动开发(BDD)的工具,您可以编写由代码(Java)备份的故事 ,然后运行它们。 JBehave可用于更高级别的测试,例如功能测试。 使用JBehave,可以更轻松地测试系统中的流。 它遵循给定,何时,然后顺序。 如果继续进行下一步,它可能是一个很好的交流工具。 产品负责人可以编写方案,如果所有方案都是绿色的,则在迭代结束时,我们传递了done的定义 。 黄瓜是另一个BDD工具。
依赖注入
为了拥有可测试的代码,除其他外,您需要练习DI(依赖注入)。 原因很简单:如果在被测类的构造函数(或方法)中实例化依赖项,那么如何模拟它? 如果您无法模拟依赖项,那么您将受到约束。 而且您无法模拟不同的情况。 许多应用程序都将Spring用作DI容器,但是较少的开发人员会利用使用注入进行测试的优势。
指标
在您的CI环境中使用SONAR 。 使用cobertura或其他工具检查代码覆盖率。 使用Jenkins / Hudson / Other CI工具进行自动化。
集成开发环境
您的IDE可以帮助您编写测试。
对于日食,我有两个建议:
- MoreUnit是一个很棒的插件,可以帮助更快地编写测试。
- 在Eclipse中,CTRL + Space可以给您提示并填充导入。 但不是静态导入。 大多数(全部?)库都使用静态导入。 因此,您可以将测试库添加为收藏夹,然后eclipse会为您填充它们。
聚甲醛
这是用于测试库的POM的一部分。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.startapp.CouchRunner.GetUserProfile</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
您可以检查方法/模拟调用的顺序。
您可以使用概要文件将单元测试与集成测试分开。
翻译自: https://www.javacodegeeks.com/2014/11/its-all-about-tests-part-3.html