Single test experience
Single test is one of the indispensable links in the standard software development process. No great programmer can avoid making mistakes and writing buggy programs. Single testing is used to detect bugs. JUnit and TestNG are two well-known single test frameworks in the Java camp. However, writing a single test in Java is tedious and takes a lot of time to mock various parameters, so a single test is often considered a chicken side and can take a lot of time. During my recent internship, I encountered some pitfalls and found that developing a good single-test habit is necessary for systematic development.
Background:
The current development projects are basically based on Dubbo for remote service invocation. Due to the complexity of my own project (mainly because the conversion between internal entity classes is too complex and the system depends on services so that the call chain is too long), I cannot simply use postman tools for interface testing, and the internal development of the project is too large and often needs to push the local code to container deployment. A small bug fix can cost a lot of money to deploy (usually 15 minutes). Recently, the second project was modified for more than 10 times due to the NPE transmission problem, so it is conceivable that the deployment cost is very high. Thus understand the necessity of single test.
Our department mainly uses Spook + Grooy for unit testing. A brief introduction to clean unit testing using Grooy and Spook and one of the pitfalls of writing a single test today.
The original code was tested with junit4 and Mockito, and it was found that the code was often large and redundant for the judgment of specific conditions. Looking at previous code, it is often found that most of the tests are non-null judgments of results. If you encounter multiple conditions, you need a lot of when() thenReturn() to get specific results.
The advantages of spook for single test are simple template, simple code, and clear judgment for specific data.
The main templates for single testing with spook are Expect -where, when-then-thrown, setup-give-when -then[-where]
For example: this is a recently written example that can be thought of simply as setup-given-when-then
def "testFXSupplierName"() {setup:
def shopClient = Mock(ShopClient)
OrderSearchServiceImpl service = new OrderSearchServiceImpl()
service.shopClient = shopClient
when:
List<OrderBO> orderBOList=[new OrderBO(xxxx:new xxxx(extra: ["xxxx":2].tags:["xxxx":true]))]
OrderPageBO orderPageBO=new OrderPageBO(list: orderBOList)
shopClient.queryShopMetaInfos(_) >> new ShopMetaInfosQueryResult(shopMetaInfos:[new ShopMetaInfo(xxxx: 2.xxxxx: "test")])
service.supplierOrderPutSupplierName(orderBOList)
then:
orderPageBO.getList().get(0).getOrderMain().getExtra().get("xxxx") = ="test"
}
Copy the code
Setup: Creates the entity class to mock
Given: Establishes the returned object entity
When: Mock entity method call or method call to assign values to object entities created in Given
Then: Comparison of results
Where: Add multiple conditions
That’s another example
@Unroll(a)// This side is the marking information can be marked
def 'presell_normal'() {
setup:
def tradeSku = new TradeSku(preSale: presell)
def goods = new Goods(num: num, price: price)
def icGoods = new IcGoods(depositType: depositType,
depositRatio: depositeRatio,
deposit: deposite)
tradeSku.setIcGoodsPreSaleInfo(icGoods)
expect:
def value = TradeGroupConvertUtil.preSell(tradeSku, goods)
value == res
where:
presell << [1.1.1.1.1.1.1.1.0.0]
depositType << [0.0.0.0.1.1.1.1.0.0]
depositeRatio << [40.20.30.80.1.2.3.4.0.0]
num << [1.2.3.4.1.2.3.4.0.0]
price << [100.100.100.100.100.100.100.100.0.0]
deposite << [40.20.30.80.1.2.200.4.0.0]
res << ['40'.'40'.'90'.'320'.'1'.'4'.'300'.'16'.' '.' ']}Copy the code
You can use this for exception scenarios
def 'getpreSell_error'() {
setup:
def tradeSku = new TradeSku(preSale: presell)
def goods = new Goods(num: num, price: price)
def icGoods = new IcGoods(depositType: depositType,
depositRatio: depositeRatio,
deposit: deposite)
tradeSku.setIcGoods(icGoods)
when:
TradeGroupConvertUtil.preSell(tradeSku, goods)
then:
def ex = thrown(Exception)
expect:
ex.message == errorMessage
where:
presell << [1.1.1.1.1.1,]
depositType << [0.0.0.0.1.1,]
depositeRatio << [- 12.0.100.120.200.300]
num << [1.2.3.4.1.2]
price << [100.100.100.100.100.100]
deposite << [40.20.30.80.null.0]
errorMessage <<
["XXXXXX Abnormal Information"."XXXXXX Abnormal Information"."XXXXXX Abnormal Information"."XXXXXX Abnormal Information"."XXXXXX Abnormal Information"."XXXXXX Abnormal Information"]}Copy the code
Here are the three main examples. Cases that require exceptions to be thrown cannot be used together with normal cases. You need to distinguish between two test methods.
Pits encountered:
Write a single test tonight, modeled after the original old code in the project. Mockito annotation is used in the mock code, and it is found that the class can be obtained by annotation. However, for grooy method calls, the assignment value is always null, but the injected class is not empty, otherwise NPE problems will be reported. This was later fixed by reusing mock() injection. The conclusion is that the method of injection is the same as the method of assignment.