單元測試和集成測試中的問題及一些可用技巧

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);
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章