源码 github: https://github.com/lotusfan/junit-demo test目录
JUnit
JUnit 5的运行条件是Java 8环境。
JUnit4 与 JUnit 5 常用注解对比
JUnit4 | Junit5 | 说明 |
---|---|---|
@Test | @Test | 表示该方法是一个测试方法 |
@BeforeClass | @BeforeAll | 表示使用了该注解的方法应该在当前类中所有使用了@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之前 执行; |
@AfterClass | @AfterAll | 表示使用了该注解的方法应该在当前类中所有使用了@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之后执行; |
@Before | @BeforeEach | 表示使用了该注解的方法应该在当前类中每一个使用了@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之前 执行 |
@After | @AfterEach | 表示使用了该注解的方法应该在当前类中每一个使用了@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之后 执行 |
@Ignore | @Disabled | 用于禁用一个测试类或测试方法 |
@Category | @Tag | 用于声明过滤测试的tags,该注解可以用在方法或类上;类似于TesgNG的测试组或JUnit 4的分类。 |
@Parameters | @ParameterizedTes | 表示该方法是一个参数化测试 |
@RunWith | @ExtendWith | @Runwith就是放在测试类名之前,用来确定这个类怎么运行的 |
@Rule | @ExtendWith | Rule是一组实现了TestRule接口的共享类,提供了验证、监视TestCase和外部资源管理等能力 |
@ClassRule | @ExtendWith | @ClassRule用于测试类中的静态变量,必须是TestRule接口的实例,且访问修饰符必须为public。 |
AssertJ
示例
class People {
public People(int age, String name) {
this.age = age;
this.name = name;
}
public People() {
}
int age;
String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
assertThat 断言的构造
/**
* AbstractObjectAssert:as 描述断言判定失败时自定义内容
*
*/
@Test
public void assertThatTest() {
People people = new People();
people.setAge(10);
people.setName("fan");
assertThat(people.getAge()).as("check age %d", people.getAge()).isEqualTo(10);
}
filteredOn 过滤器
/**
* ListAssert: filteredOn
* contains、containsAnyOf、containsOnlyOnce
*
*/
@Test
public void assertThatFilteredOn() {
People people = new People(10, "a");
People people1 = new People(20, "b");
List peopleList = Arrays.asList(people, people1);
//filteredOn(String propertyOrFieldName,FilterOperator> filterOperator)
//FilterOperator: 构造 InFilter.in、NotFilter.not、NotInFilter.notIn
assertThat(peopleList).filteredOn("age", in(10)).containsOnly(people);
assertThat(peopleList).filteredOn(p -> p.getAge() == 20).containsOnly(people1);
}
inPredicate
/**
* PredicateAssert: accepts 接收数据,如果满足test条件校验成功
*/
@Test
public void assertThatInPredicate() {
List list = Arrays.asList("aaa", "bbb");
Predicate> predicate = t -> {
System.out.println(t);
return t.contains("aaa") || t.contains("bbb");
};
assertThat(predicate).accepts(list);
assertThat((Predicate) p -> true).rejects("bbbb");
/**
* 于此类似的还有AssertJ封装了IntPredicate、LongPredicate、DoublePredicate
*/
assertThat((IntPredicate) pi -> pi > 0).accepts(3, 4);
}
InOptional
/**
* OptionalAssert:isPresent、isNotEmpty、containst等
*/
@Test
public void assertThatInOptional() {
// Optional op = Optional.ofNullable(new People());
Optional op = Optional.ofNullable("aaa");
// assertThat(op).isEmpty();
assertThat(op).isNotEmpty().contains("aaa");
}
extracting、tuple 元组组合
@Test
public void assertThatExtracting() {
People people = new People(10, "a");
People people1 = new People(20, "b");
List peopleList = Arrays.asList(people, people1);
assertThat(peopleList).extracting("name", "age").contains(tuple("a", 10)).doesNotContain(tuple("c", "33"));
}
/**
* tuple只能用在extracting后,创建了一个元组
* Utility method to build nicely a {@link org.assertj.core.groups.Tuple} when working with
* {@link org.assertj.core.api.IterableAssert#extracting(String...)} or
* {@link org.assertj.core.api.ObjectArrayAssert#extracting(String...)}
*
*/
@Test
public void assertThatTuple() {
People p1 = new People(10, "a");
People p2 = new People(20, "b");
List peopleList = Arrays.asList(p1, p2);
assertThat(peopleList).extracting("name", "age").contains(tuple("a", 10));
}
entry
/**
* MapAssert: contains、containsAnyOf、containsOnly、containsKeys、containsOnlyKeys、containsValues
*
* 注:
* contains(Map.Entry extends KEY, ? extends VALUE>... entries) 入参为Map.entry,所以需要调用
* Assertions: MapEntry entry(K key, V value) 构造
*/
@Test
public void assertThatEntry() {
Map map = new HashMap<>();
map.put("akey", new People(10, "a"));
assertThat(map).containsAnyOf(entry("bkey", new People(20, "b")), entry("akey", map.get("akey")));
}
atIndex List坐标
/**
* Assertions: atIndex 使用在 AbstractListAssert: contains(ELEMENT value, Index index) 方法中的index构造
* 可能还有其它地方使用到
*/
@Test
public void assertThatAtIndex() {
People p1 = new People(10, "a");
People p2 = new People(20, "b");
List peopleList = Arrays.asList(p1, p2);
assertThat(peopleList).extracting("name").contains("a", atIndex(0));
}
returns 对象方法返回
/**
* AbstractObjectAssert: returns 验证 入参对象 调用方法返回值
* from 构造 Function
*/
@Test
public void assertThatReturns() {
assertThat(new People(10, "a")).returns("b", from(People::getName));
}
condition 自定义断言
/**
* 自定义断言
* AbstractAssert is,isNot,has,doesNotHave
* AbstractObjectArrayAssert are,have,doNotHave,areAtLeast,haveExactly 集合条件
*
* condition组合 allOf anyOf doesNotHave not
*/
@Test
public void assertThatCondition() {
People p1 = new People(10, "a");
People p2 = new People(20, "b");
List peopleList = Arrays.asList(p1, p2);
Condition c1 = new Condition<>(people -> people.getName().equals("a"), "condition 1");
Condition c2 = new Condition<>(people -> people.getName().equals("b"), "condition 2");
Condition c3 = new Condition<>(people -> true, "condition 3");
Condition c4 = new Condition<>(people -> false, "condition 4");
assertThat(peopleList).have(not(c4));
// assertThat(peopleList).have(anyOf(c1, c2, c3));
}
filter
/**
* Assertions:filter 按条件筛选数据并重新生成列表进行校鸡
*/
@Test
public void assertThatFilter() {
People p1 = new People(10, "a");
People p2 = new People(20, "b");
List peopleList = Arrays.asList(p1, p2);
assertThat(filter(peopleList).and("name").equalsTo("a").and("age").equalsTo("10").get()).extracting("name").contains("b");
}
fail
/**
* 异常
* @throws Exception
*/
@Test(expected = RuntimeException.class)
public void expectedException() throws Exception {
throw new Exception();
}
@Test(expected = RuntimeException.class)
public void expectedExceptionSupper() {
throw new RunExceptionSub();
}
@Test
public void assertThatFail() {
try {
fail("异常", RuntimeException.class);
throwRuntimeException();
} catch (Exception e) {
}
}
private void throwRuntimeException() {
throw new RuntimeException();
}
timeOut
/**
* 超时
*/
@Test(timeout = 1000)
public void timeOut() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
其它
/**
* AbstractBigDecimalAssert> assertThat(BigDecimal actual)
* AbstractBigIntegerAssert> assertThat(BigInteger actual)
* AbstractUriAssert> assertThat(URI actual)
* AbstractUrlAssert> assertThat(URL actual)
* AbstractBooleanAssert> assertThat(boolean actual)
* AbstractBooleanArrayAssert> assertThat(boolean[] actual)
* AbstractByteAssert> assertThat(byte actual)
* AbstractByteArrayAssert> assertThat(byte[] actual)
* AbstractCharacterAssert> assertThat(char actual)
* AbstractCharacterAssert> assertThat(Character actual)
* ClassAssert assertThat(Class> actual)
* AbstractDoubleAssert> assertThat(double actual)
* AbstractDoubleArrayAssert> assertThat(double[] actual)
* AbstractFileAssert> assertThat(File actual)
*
* FutureAssert assertThat(Future actual)
*
* AbstractInputStreamAssert, ? extends InputStream> assertThat(InputStream actual)
* AbstractFloatAssert> assertThat(float actual)
* AbstractFloatArrayAssert> assertThat
* AbstractIntegerAssert> assertThat(int actual)
*
* , ELEMENT, ELEMENT_ASSERT extends AbstractAssert>
* FactoryBasedNavigableIterableAssert, ACTUAL, ELEMENT, ELEMENT_ASSERT> assertThat(Iterable extends ELEMENT> actual,
* AssertFactory assertFactory)
*
* , ELEMENT_ASSERT extends AbstractAssert>
* ClassBasedNavigableListAssert, ACTUAL, ELEMENT, ELEMENT_ASSERT> assertThat(List extends ELEMENT> actual,
*
*/
注:jdk中所有对象基本都封了特定的Assert,基本类型还封装了ArrayAssert 使用的时候可以因需而定
Hamcrest
Hamcest提供了一套匹配符Matcher,这些匹配符更接近自然语言,可读性高,更加灵活。
示例
anything
/**
* 总是匹配成功
*/
@Test
public void hamcrestAnything() {
assertThat("failed message anything", "aa", anything());
assertThat("failed message anything", null, anything());
}
allOf
/**
* 全部 Marcher 匹配 才通过
*/
@Test
public void hamcrestAllof() {
assertThat("aaa", allOf(notNullValue(), isEmptyString()));
}
anyOf
/**
* 任意 Marcher 匹配 则通过
*/
@Test
public void hamcrestAnyof() {
assertThat("aaa", anyOf(notNullValue(), isEmptyString()));
}
not
/**
* 不等
*/
@Test
public void hamcrestNot() {
assertThat("aaa", not("aaa"));
}
equalTo
/**
* 对象 equalTo 返回是否相等
*/
@Test
public void hamcrestEqualTo() {
Bicycle bicycle1 = new Bicycle() {
@Override
public boolean equals(Object obj) {
return true;
}
};
Bicycle bicycle2 = new Bicycle() {
@Override
public boolean equals(Object obj) {
return true;
}
};
assertThat(bicycle1, equalTo(bicycle2));
}
hasToString
/**
* 对象 toString 返回 是否相等
*/
@Test
public void hamcrestHasToString() {
Bicycle bicycle = new Bicycle() {
@Override
public String toString() {
return "bicycle";
}
};
assertThat(bicycle, hasToString("bicycle"));
}
null
/**
* 对象 是否为null
*/
@Test
public void hamcrestNull() {
assertThat(null, nullValue());
assertThat("aaa", notNullValue());
}
hasProperty
/**
* 测试JavaBeans属性,而不是属性值
*/
@Test
public void hamcrestHasProperty() {
Bicycle bicycle = new Bicycle();
assertThat(bicycle, hasProperty("type"));
}
array
/**
* 数组的每一项 都要配置验证 Matcher
*/
@Test
public void hamcrestArray() {
String[] strings = new String[]{"aaa", "bbb"};
assertThat(strings, arrayWithSize(2));
assertThat(strings, array(notNullValue(), is("bbb")));
}
hasEntry
/**
* Map 包含
* hasEntry, hasKey, hasValue
*/
@Test
public void hamcrestHasEntry() {
Map map = new HashMap<>();
map.put("name", "zhang");
assertThat(map, hasKey("name"));
assertThat(map, hasValue("zhang"));
assertThat(map, hasEntry("name", "zhang"));
}
hasItem
/**
* 集合 包含
*/
@Test
public void hamcrestHasItem() {
List list = Arrays.asList("aaa", "bbb");
assertThat(list, hasItem("aaa"));
assertThat(list, hasItems("aaa", "bbb"));
}
equalToIgnoringCase
/**
* 忽略 字符串 大小写
*/
@Test
public void hamcrestEqualToIgnoringCase() {
assertThat("aAa", equalToIgnoringCase("AAA"));
}
equalToIgnoringWhiteSpace
/**
* 只忽略字符串 前、后 的空白,不包含 中间空格
*/
@Test
public void hamcrestEqualToIgnoringWhiteSpace() {
assertThat("aaaaaa ", equalToIgnoringWhiteSpace(" aaaaaa"));
}
containsString
/**
* containsString, endsWith, startsWith - 测试字符串匹配
*/
@Test
public void hamcrestContainsString() {
assertThat("abc", containsString("b"));
}
is
/**
* 提高可读性
*/
@Test
public void hamcrestIs() {
assertThat("aaaa", is("bbbb"));
}
isIn
@Test
public void hamcrestIsIn() {
List strings = Arrays.asList("aaa", "bbb");
assertThat("aaa", isIn(strings));
}
Mockito
官方示例 http://static.javadoc.io/org.mockito/mockito-core/2.20.0/org/mockito/Mockito.html
示例
class Car {
String type;
String code;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getCarString(String year, String day) {
return type + code + "_" + year + day;
}
}
public class MockitoTest {
Car car;
@Before
public void beforeTest() {
car = mock(Car.class);
}
}
mock
/**
* mock类是不具有真实类的方法实现的
*/
@Test
public void mockObject() {
Car car = mock(Car.class);
System.out.println(car.getCarString("2222" ,null));
}
when
/**
* OngoingStubbing when(T methodCall)
*
* OngoingStubbing: thenReturn、thenThrow、thenCallRealMethod、thenAnswer、getMock
*
* 多个thenReturn会按调动顺序依次返回,直到最后
*/
@Test
public void mockWhen() {
when(car.getCode()).thenReturn("111").thenReturn("2222");
System.out.println(car.getCode());
System.out.println(car.getCode());
System.out.println(car.getCode());
}
thenAnswer
/**
* thenAnswer可以接收传入的参数,自定义方法返回
* ArgumentMatchers: anyString()
*/
@Test
public void mockThenAnswer() {
when(car.getCarString(anyString(), anyString())).thenAnswer(invocation -> {
System.out.println(invocation.getArgument(0) + "_" + invocation.getArgument(1));
return "456789";
});
System.out.println(car.getCarString("11", "333"));
}
verify
/**
* verify(T mock, VerificationMode mode)
*
* VerificationMode 构造 times(2), atLeastOnce(), atLeast(), atMost(), only(), never(), atLeastOnce(), description()
*/
@Test
public void mockVerify() {
verify(car, times(2).description("get Code not appear 2 times")).getCode();
}
reset
/**
* 重置
*/
@Test
public void mockReset() {
when(car.getCode()).thenReturn("aaaa");
System.out.println(car.getCode());
reset(car);
System.out.println(car.getCode());
}
spy
/**
* 监控真实类,使用do-when创建mock
*
*/
@Test
public void spyObject() {
Car carReal = new Car();
Car spyCar = spy(carReal);
System.out.println(spyCar.getCarString(null, null));
}
doThrow
/**
* want void method throws Exception
* doThrow可以添加多个Exception被依次调动,直到最后一个
*/
@Test
public void mockDoThrow() {
doThrow(new RuntimeException("first"), new RuntimeException("second")).when(car).setCode(anyString());
try {
car.setCode("aaa");
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
car.setCode("bbb");
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
car.setCode("ccc");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
doCallRealMethod
/**
* 调用原始的方法实现
*/
@Test
public void mockDoCallRealMethod() {
when(car.getCode()).thenReturn("bbbb");
System.out.println(car.getCode());
doCallRealMethod().when(car).getCode();
System.out.println(car.getCode());
}
doAnswer
/**
* void method 提供一个接收参数的自定义方法
*/
@Test
public void mockDoAnswer() {
doAnswer(invocation -> null).when(car).setCode(anyString());
car.setCode("3333");
}
doNothing
/**
* void method 不做任何操作
*/
@Test
public void mockDoNothing() {
doNothing().doThrow(new RuntimeException()).when(car).setType(anyString());
car.setType("333");
car.setType("4444");
}
spy mock数据
/**
* 使用spy来监控真实的对象,需要注意的是此时我们需要谨慎的使用when-then语句,而改用do-when语句
*/
@Test
public void mockDoReturn() {
Car carReal = new Car();
Car spyCar = spy(carReal);
doReturn("spy object call mock data").when(spyCar).getCode();
System.out.println(spyCar.getCode());
}
PowerMock
不支持junit 5
需要依赖EasyMock或者Mockito
最高支持到 Mockito 2.8.9(spring boot 2.0.4.RELEASE 中使用的Mockito版本为2.15)
PowerMock依赖Mockito版本
PowerMock version | Mockito version |
---|---|
1.7.x | 2.8.0 - 2.8.9 |
1.7.0RC4 | 2.7.5 |
1.7.0RC2 | 2.4.0 |
1.6.5 - 1.7.0RC | 2.0.0-beta - 2.0.42-beta |
1.6.2 - 2.0 | 1.10.8 - 1.10.x |
1.5.0 - 1.5.6 | 1.9.5-rc1 - 1.9.5 |
1.4.10 - 1.4.12 | 1.9.0-rc1、1.9.0 |
1.3.9 - 1.4.9 | 1.8.5 |
1.3.7、1.3.8 | 1.8.4 |
1.3.6 | 1.8.3 |
1.3.5 | 1.8.1、1.8.2 |
1.3 | 1.8 |
1.2.5 | 1.7 |
pom依赖
org.powermock
powermock-api-mockito2
1.7.4
test
org.powermock
powermock-module-junit4
1.7.4
test
如果使用spring boot 2.0.0,则需要将spring boot test中的mockito依赖去掉,重新添加mockito 2.8.9
org.mockito
mockito-core
2.8.9
org.springframework.boot
spring-boot-starter-test
test
org.mockito
mockito-core
依赖注解
@RunWith(PowerMockRunner.class)
@PrepareForTest({xx.class})
示例
public class Target {
private String parameter;
public Target() {
}
public Target(String parameter) {
this.parameter = parameter;
}
public String getParameter() {
return parameter;
}
private String privateMethod() {
return "";
}
public String privateMethodWithParam(String str) {
return "";
}
public String callPricateMethod() {
return privateMethod();
}
public String callPricateMethodWithParam() {
return privateMethodWithParam("");
}
public static String staticMethod() {
return "";
}
}
public final class FinalTarget {
public final String print() {
return "";
}
}
PowermockTest static方法、private方法、construct方法
添加注解
@RunWith(PowerMockRunner.class)
@PrepareForTest({Target.class})
public class PowermockTest {}
static method
/**
* mock 静态方法
*
*/
@Test
public void staticMock() {
mockStatic(Target.class);
when(Target.staticMethod()).thenReturn("static method mock data");
System.out.println(Target.staticMethod());
}
pricate method
/**
* mock private方法
*
* @throws Exception
*/
@Test
public void privateMock() throws Exception {
Target target = new Target();
Target targetSpy = spy(target);
when(targetSpy, "privateMethod").thenReturn("private method mock deta");
System.out.println(targetSpy.callPricateMethod());
}
/**
* mock 有入参private方法
*
* @throws Exception
*/
@Test
public void privateMockWhithParam() throws Exception {
Target targetSpy = spy(new Target());
when(targetSpy, "privateMethodWithParam", anyString()).thenReturn("private method where parameter mock data");
System.out.println(targetSpy.callPricateMethodWithParam());
}
/**
* stub 方式 mock private方法
*
*/
@Test
public void privateStubMock() {
Target targetSpy = spy(new Target());
stub(MemberMatcher.method(Target.class, "privateMethod")).toReturn("privvate method stub mock data");
System.out.println(targetSpy.callPricateMethod());
}
construct method
/**
* mock 构造函数
*
* @throws Exception
*/
@Test
public void constructMock() throws Exception {
Target mockTarget = new Target("mock data");
// whenNew(Target.class).withNoArguments().thenReturn(new Target("mock data")); 不可以这么写
whenNew(Target.class).withNoArguments().thenReturn(mockTarget);
Target target = new Target();
System.out.println(target.getParameter());
}
spring boot test
spring-boot-starter-test 添加的maven引用
JUnit — The de-facto standard for unit testing Java applications.
Spring Test & Spring Boot Test — Utilities and integration test support for Spring Boot applications.
AssertJ — A fluent assertion library.
Hamcrest — A library of matcher objects (also known as constraints or predicates).
Mockito — A Java mocking framework.
JSONassert — An assertion library for JSON.
JsonPath — XPath for JSON.
spring boot test 注解
基本注解 (类注解)
@SpringBootTest() 创建springApplication上下文,支持SpringBoot特性(自动配置)
- 参数class = ? 启动的配置类 (没有默认值,必须指定)
- 参数webEnvironment = ?
- Mock(默认值): 加载WebApplicationContext并提供Mock Servlet环境
- RANDOOM_PORT: 加载EmbeddedWebApplicationContext并提供servlet环境,内嵌服务的监听端口是随机的
- DEFINED_PORT: 加载EmbeddedWebApplicationContext并提供servlet环境,内容服务的监听端口是定义好的。默认端口是8080
- NONE: 加载ApplicationContext,启动SpringApplication时,不支持Servlet环境
@RunWith(SpringRun.class) 运行Junit并支持Spring-test特性
@TestConfiguration 单元测试类使用的配置标签,没有强制要求
spring mvc注解(类注解)
@WebMvcTest 自动加载Spring MVC配置、MockMvc配置、并扫描注解类
注:在没有@WebMvcTest注解时,使用@AutoConfiureMockMvc可以自动配置MockMvc;@Component将不被扫描
mock bean注解 (成员变量 和 方法 注解,可以在配置文件中提供bean)
@MockBean 声明需要模拟的服务
@SpyBean 定制化需要模拟服务的某个方法,即部分方法可以mock,部分方法可以调用真实方法
事务回滚 (类 和 方法 注解)
@Transactional 开启事务,回滚测试方法对数据库的改变
对外接口文档(类注解)
@AutoConfigureRestDocs
示例
Base Test
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JunitDemoApplication.class)
@Import(TestConfig.class)
@Log
public class BootTest {
@Autowired
TestBean testBean;
@Test
public void bootTest() {
log.info("boot test success");
testBean.print();
}
}
Mock Bean Test
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JunitDemoApplication.class)
@Log
public class MockBeanTest{
@MockBean
RemoteDubboService remoteDubboService;
@Autowired
LocalService localService;
@Before
public void init() {
log.info("mock bean before");
when(remoteDubboService.getMsg()).thenReturn("mock remote dubbo Service");
}
@Test
public void mockBean() {
log.info("mock bean");
localService.localPrint();
}
}
Spy Bean Test
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JunitDemoApplication.class)
@Log
public class SpyBeanTest{
@SpyBean
LocalSpyService localSpyService;
@Before
public void init() {
doReturn("mock local spy out").when(localSpyService).out();
}
@Test
public void spyTest() {
log.info("syp bean");
log.info(localSpyService.in());
log.info(localSpyService.out());
}
}
Spring MVC Test
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = JunitDemoApplication.class)
@WebMvcTest(MVCTest.class)
@Import(MybatisConfig.class)
@AutoConfigureRestDocs
@Log
public class SpringMVCTest {
@Autowired
MockMvc mockMvc;
@Test
public void mvcTest() throws Exception {
this.mockMvc.perform(get("/mvc/enter").accept(MediaType.ALL))
.andExpect(status().isOk())
.andDo((document("mvcTest", responseFields(fieldWithPath("name").description("合同地址")))));
}
}
Transactional Roll Back Test
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JunitDemoApplication.class)
public class TransactionalRollBack{
@Autowired
MapperTest mapperTest;
@Test
@Transactional(timeout = 100)
public void save() {
mapperTest.save("12345678");
}
}