How to write assert correctly

Now let's talk about assertions.



Assertions are one of the things taken for granted, so everything here would seem to be experts. Someone uses the built-in Java or Junit, someone uses advanced libraries, someone builds his own.



But let's try to approach this more seriously. How, in fact, right?



First, assertions duplicate the specification. If the specification shouldReturnValidOrder is specified, then this should be checked, and not assertNotNull.



Secondly, assertions should be easy to read. That is, instantly, comparing with the specification.



If there are too many assertions (for my extreme taste, there are many more than one, but we will put a limit of at least five to seven), then the test ceases to be understandable. For good, each test should have only one reason to fall.



Assets can be directed to: a primitive field, an object, a collection, rarely a function or thunk.



The more complex the object, the more complex and diverse the assertions will be, and the more difficult it will be to read them and find errors.



Positive statements read better than double negative ones.



Standard methods are read better than custom helpers. (Who knows who and where tested the library of helpers). And the assertions should be in the body of the test method, and not somewhere in the back of the test helpers (the sonar rightly swears at the lack of assertions).

Assets about fields are more understandable than assertions about objects and especially collections.



In the case of nested fields, it makes sense to test completely.



Not



assertNotNull(order.getCustomer().getName)
      
      





but



 assertNotNull(order) assertNotNull(order.getCustomer()) assertNotNull(order.getCustomer().getName())
      
      





Assers do not just stupidly check what is returned from the method, but influence it. And if we can change it, then we need to change it.



It is relatively difficult to make assertions about collections. Do they contain zeros, are they sorted, exactly how are they sorted, how are elements checked for equality, etc. - all this makes collections difficult objects for assertions, especially when there is logic in the method related to the elements of collections.



Therefore, a method like:



 List<Order> getOrderList(List<OrderDao> orderDaoList){ return orderDaoList.stream().map(orderDao=>order.name(orderDao.getName).build()).collect(Collectors.toList()) }
      
      





it is easy to split into two, one transformer orderDao => order and test it separately, and the second will test the mapping of collections on the abstract mapper, and we can test it on the stub.



 List<Order> getOrderList(List<OrderDao> orderDaoList, OrderDaoToOrderMapper mapper){ return orderDaoList.stream().map(mapper::map).collect(Collectors.toList()) }
      
      





On the other hand, collections lend themselves well to typification and expansion, i.e. we can relatively easily make a collection of its own type with all the tested properties, co- and contravariance, etc. Therefore, instead of a generic List, we could make our own OrderList, or OrderSet, or OrderSortedSet, and the more specific, the better. And the tests will become easier.



Making statements about functions is not much more complicated than about objects, and they are well typed in some languages, so you can probably recommend a better typing, i.e. instead of Func <Order, Order> return some OrderComparator.



All Articles