Junit4
说起Java中的单元测试,我觉得大家首先想到的应该是Junit,比如下面这样的一个类和方法:
public class Calculator {
public int evaluate(String expression) {
int sum = 0;
for (String summand: expression.split("\\+"))
sum += Integer.valueOf(summand);
return sum;
}
}
复制代码
我们可以编写一个测试类 CalculatorTest.java
,来对方法Calculator.evaluate((String expression)
进行单元测试:
public class CalculatorTest {
@Test
public void evaluatesExpression() {
Calculator calculator = new Calculator();
int sum = calculator.evaluate("1+2+3");
Assert.assertEquals(6, sum);
}
}
复制代码
代码如上,可以看到,使用一个注解 @Test
和断言语句Assert.assertEquals(6, sum);
,便完成了一个单元测试的编写。
Test runners
一些常见的IDE都提供了对Junit的支持,可以直接在界面进行测试,无需我们自己去控制台run这些程序。
Junit默认的runner是:BlockJUnit4ClassRunner
,可以使用@RunWith
注解来指定runner,比如后续我们需要用到的SpringJUnit4ClassRunner
。
Assertions
上述例子中,要验证Calculator.evaluate
的正确性,我们使用到了断言语句 Assert.assertEquals(6, sum);
,在平常的单元测试中,我们都需要编写大量的断言语句。对于所有的基本类型,Junit都提供了断言方法,就像下面这样:
public class AssertTests {
@Test
public void testAssertNull() {
assertNull("should be null", null);
}
@Test
public void testAssertTrue() {
assertTrue("failure - should be true", true);
}
}
复制代码
Spring与Junit
在spring中,Spring TestContext Framework
提供了对Junit的支持,通过使用@RunWith(SpringJUnit4ClassRunner.class)
或@RunWith(SpringRunner.class)
注解,我们便能实现一个标准的Junit测试,并且能获取到Spring TestContext framework的支持,比如获取spring context、事务管理、依赖注入等等。
@ContextConfiguration
@ContextConfiguration用来加载和配置一个ApplicationContext
,可以用指定xml的方式,也可以指定一个config class:
@ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
// class body...
}
@ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
// class body...
}
复制代码
即使用@RunWith(SpringRunner.class)
和@ContextConfiguration
,就可以起一个spring的环境并进行测试了,如下:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CalculatorTest.TestConfig.class)
public class CalculatorTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void application() {
Assert.assertNotNull(applicationContext);
Assert.assertNotNull(applicationContext.getBean("testBean"));
}
@Configuration
public static class TestConfig {
@Bean
public Object testBean() {
return new Object();
}
}
}
复制代码
Mock
在真实的测试场景中,一个类往往会引用一个至多个bean,这些bean往往又会引用其他的bean,如果想在配置文件或类中配置完所有的bean,发现会越写越多,最后可能需要一份全量的bean配置了。
public abstract class AbstractSplitPkgStrategy extends AbstractCostSplitStrategy {
@Resource
private ISelfScItemAtomService selfScItemAtomService;
@Resource
private GoodsCenterService goodsCenterService;
@Resource
private CostItemSplitMapper costItemSplitMapper;
@Resource
private SplitPkgByPcsMetaGenerateStrategy pcsStrategy;
@Resource
private LstPackageReadService lstPackageReadService;
@Resource
private TairManager commonTairManager;
//方法...
}
复制代码
比如上面是我们要进行测试的一个类,当我尝试在xml或者config中配置它引用的bean的时候,我发现这些bean又引入了其他bean,然后得需要一层一层往下写配置。
Mockito
针对上述问题,我们可以使用Mockito来mock掉我们需要的bean。mock的配置也有多种方式,如下是两种配置方式:
使用xml进行配置:
//xml配置
<bean id="calculator" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.zuqiang.junit.Calculator"/>
</bean>
//test
@RunWith(SpringRunner.class)
@ContextConfiguration("/context.xml")
public class CalculatorTest {
@Autowired
private Calculator Calculator;
@Test
public void application() {
Assert.assertNotNull(Calculator);
}
}
复制代码
使用config class:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CalculatorTest.MockConfig.class)
public class CalculatorTest {
@Autowired
private Calculator calculator;
@Test
public void application() {
Assert.assertNotNull(calculator);
}
@Configuration
public static class MockConfig{
@Bean
public Calculator calculator() {
return Mockito.mock(Calculator.class);
}
}
}
复制代码
mock的使用:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CalculatorTest.MockConfig.class)
public class CalculatorTest {
@Autowired
private Calculator calculator;
@Before
public void before() {
Mockito.when(calculator.evaluate(Mockito.anyString())).thenReturn(1);
}
@Test
public void application() {
Assert.assertEquals(1,calculator.evaluate("1+2"));
Assert.assertEquals(1,calculator.evaluate("2+2"));
}
@Configuration
public static class MockConfig {
@Bean
public Calculator calculator() {
return Mockito.mock(Calculator.class);
}
}
}
复制代码
断言
在上面,我们已经看到了Junit提供的断言方法,对于一些简单的场景,我觉得没有什么问题。但是在一些复杂的场景下,可能会导致代码晦涩难懂。所以最后给大家介绍一个更好用的Java断言框架— AssertJ
。
比如下面这个比较简单的test:
@Test
public void test() throws Exception {
//Arrange
List<String> strings = Lists.newArrayList("a","b","c");
//Act
String removedString = strings.remove(0);
//Assert
assertThat(strings, containsInAnyOrder("b", "c"));
}
复制代码
用AssertJ之后的写法像下面这样:
@Test
public void test() throws Exception {
//Arrange
List<String> strings = Lists.newArrayList("a","b","c");
//Act
String removedString = strings.remove(0);
//Assert
assertThat(strings).contains("b", "c");复制代码