单元测试和集成测试中的问题及一些可用技巧

1、在涉及到有HTTP发送请求的测试,而所要访问的接口又没有准备好,就需要用到 mockServer:
当一个测试类中有多个测试用例的时候,必须使用static. 测试发现,测试类中不使用static的字段,每个测试用例执行的时候都会创建新的对象,会出现端口创建异常的情况,即使使用了mockServer.stop,当GET,POST都测试的时候,也会出现返回为空的情况(端口冲突)

// pom文件
<dependency>
     <groupId>org.mock-server</groupId>
     <artifactId>mockserver-netty</artifactId>
     <version>3.10.1</version>
</dependency>

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());
<!— any()表示调用方法的入参可以填任意值 —>

然后通过反射,将mock出来的对象设置到指定的位置,当执行到指定位置的时候,对象调用所用的就是mock出来的对象,调用的返回结果就是thenReturn的返回结果。

Class<CustomerServiceImpl> cla = CustomerServiceImpl.class;
Constructor<CustomerServiceImpl> 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文件引入
<dependency>
<groupId>com.github.springtestdbunit</groupId>
<artifactId>spring-test-dbunit</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.5.3</version>
<scope>test</scope>
</dependency>

// 类的监听
@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文件中,数据格式为:

// 准备数据
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<tb_consumer id="[NULL]" name="Junit_consumer" type="2" address="Junit_address"
contact_number="110" state="1" create_time="[NOW]" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
<tb_consumer id="[UUID]" name="Junit_consumer2" type="2" address="[NULL]"
contact_number="120" state="1" create_time="[NOW]" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
<tb_consumer id="[UUID]" name="Junit_consumer2" type="[NULL]" address="Junit_address"
contact_number="130" state="1" create_time="[NOW]" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
<tb_consumer id="[UUID]" name="Junit_consumer2" type="2" address="Junit_address"
contact_number="140" state="1" create_time="[NOW]" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
<tb_consumer id="[UUID]" name="Junit_consumer2" type="2" address="Junit_address"
contact_number="150" state="1" create_time="[NOW]" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
</dataset>

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 时,没填的字段将不会对比

// 期望数据
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<tb_consumer name="Junit_consumer2" type="2" address="[NULL]"
contact_number="120" state="1" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
<tb_consumer name="Junit_consumer" type="2" address="Junit_address"
contact_number="110" state="1" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
<tb_consumer name="Junit_consumer2" type="[NULL]" address="Junit_address"
contact_number="130" state="1" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
<tb_consumer name="Junit_consumer2" type="2" address="Junit_address"
contact_number="140" state="1" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
<tb_consumer name="Junit_consumer2" type="2" address="Junit_address"
contact_number="150" state="1" create_user_id="c0dc744d3aee11e892cb0025901bf22a" />
</dataset>

@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<Consumer> list = consumerMapper.selectAll();
    assertNotEquals(list.size(), 0);
  }
}

猜你喜欢

转载自blog.51cto.com/12181171/2298940