1、在涉及到有HTTP发送请求的测试,而所要访问的接口又没有准备好,就需要用到 mockServer:
当一个测试类中有多个测试用例的时候,必须使用static. 测试发现,测试类中不使用static的字段,每个测试用例执行的时候都会创建新的对象,会出现端口创建异常的情况,即使使用了mockServer.stop,当GET,POST都测试的时候,也会出现返回为空的情况(端口冲突)
// pom文件
org.mock-server
mockserver-netty
3.10.1
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.Header;
import org.mockserver.model.HttpRequest;
// 模拟的访问地址为:localhost:1002(端口自己指定)
ClientAndServer mockServer = startClientAndServer(1002);
// 防止有缓存的设置
mockServer.reset();
// 设置期望的返回结果
String expected = "{\"success\":false,\"message\":\"success\"}";
// 请求:请求方式,请求地址(可加其他参数)
HttpRequest request = request().withMethod("POST").withPath("/test");
Header header = new Header("Content-Type", "application/json;charset=utf-8");
mockServer.when(request).respond(response()
.withStatusCode(200) // 表示响应成功
.withBody(expected) // 响应体为期望值
.withHeader(header)); // 响应数据的格式及编码
访问到该地址时返回的结果是预期设定的值。
2、某些类在调用某个方法时,想要让其返回想要的数据,可以用 mock:
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
eg:
XxService abc = mock(XxService.class);
// 1、想让该实例调用方法时返回想要的结果
UserService userService = mock(UserServiceImpl.class);
when(userService.getCurUser()).thenReturn(user);
///2、想让调用方法时抛出指定异常
RedisUtil util = mock(RedisUtil.class);
doThrow(Exception.class).when(util).publish(anyString(), anyString());
然后通过反射,将mock出来的对象设置到指定的位置,当执行到指定位置的时候,对象调用所用的就是mock出来的对象,调用的返回结果就是thenReturn的返回结果。
Class cla = CustomerServiceImpl.class;
Constructor cfon = cla.getConstructor();
CustomerServiceImpl obj = cfon.newInstance();
//
Field field = cla.getDeclaredField("redisUtil");
field.setAccessible(true);
field.set(obj, util);
// 通过反射调用方法
Method method = cla.getMethod("setCustomerExtraData", Customer.class);
boolean result = (boolean) method.invoke(obj, customer);
// 保证mock的对象重新设置回原来的对象,不然后继操作会受到影响
reset(userService);
注意:如果所测试的方法中有其他自动注入的对象,也要在测试类里进行自动注入。然后通过反射给他们设置进去,不然会读取不到!
一些难以覆盖的地方都可以通过反射来进行测试。
3、测试Dao层的时候,可以用到DBUnit,主要用于测试前的数据准备,以及测试后期望值对比:
// pom文件引入
com.github.springtestdbunit
spring-test-dbunit
1.3.0
test
org.dbunit
dbunit
2.5.3
test
// 类的监听
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
@DbUnitConfiguration(dataSetLoader= XmlDataSetLoader.class)
准备数据 @DatabaseSetup(type = DatabaseOperation.INSERT,value = "classpath:/data/consumer.xml") 将需用用到的数据添加到data下consumer.xml文件中,数据格式为:
// 准备数据
dataset节点下子节点为表名。可以写入多张表,属性为表的字段,带[]字段为占位符[UUID] 为32位uuid,[NOW] 为当前时间,[NULL] 为null.
// 期望
@ExpectedDatabase(table = "tb_consumer",query="select * from tb_consumer where name like 'Junit%'", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED , value = "data/consumerExpect.xml")
期望文件 query为期望文件与数据库中哪些数据对比,如果不填将与数据库中所有数据对比, table 为表名,value为期望文件,填写期望数据,当assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED 时,没填的字段将不会对比
// 期望数据
@Transactional 事物标签,当方法执行完后,数据库中的数据将回滚,不会影响原有数据
@SpringBootTest
@RunWith(SpringJUnit4Cla***unner.class)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
@DbUnitConfiguration(dataSetLoader= XmlDataSetLoader.class)
public class ConsumerMapperDbUnitTest {
@Autowired
private ConsumerMapper consumerMapper;
@Test
@Transactional(rollbackFor = Exception.class)
@DatabaseSetup(type = DatabaseOperation.INSERT, value = "data/consumer.xml")
@ExpectedDatabase(table = "tb_consumer", query = "select * from tb_consumer where name like 'Junit%'", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED,
value = "data/consumerExpect.xml")
public void testInsert() {
List list = consumerMapper.selectAll();
assertNotEquals(list.size(), 0);
}
}