Posted in

3个Go mock经典误用场景,你中招了吗?

hndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndnd][hndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndndnd][hndndndndndndndndndndndndndndndndndndndndndndndndnd][hndndndndndndnd][hndndndndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndned][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndned][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndnd][hndned][hndnd][hndnd][hndnd][hndned][hndnd][hndnd][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hndned][hnd

                                                                                                                                     @     @     @     @     @     @     @     @     @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @

2.1 理解Go中依赖注入与接口抽象的作用

在Go语言中,依赖注入(DI)与接口抽象共同构建了高内聚、低耦合的程序结构。通过将组件间的依赖关系显式传递,而非在内部硬编码,系统更易于测试和维护。

接口抽象:定义行为契约

Go的隐式接口实现允许类型无需声明即满足接口,只要其方法集匹配。这促进了松耦合设计:

type Notifier interface {
    Send(message string) error
}

type EmailService struct{}

func (e *EmailService) Send(message string) error {
    // 发送邮件逻辑
    return nil
}

EmailService 自动实现 Notifier 接口,无需显式声明。这种抽象使上层模块仅依赖行为定义,而非具体实现。

依赖注入:解耦组件创建与使用

通过构造函数或方法传入依赖,提升可替换性:

type UserService struct {
    notifier Notifier
}

func NewUserService(n Notifier) *UserService {
    return &UserService{notifier: n}
}

UserService 不关心通知方式的具体实现,仅通过接口调用。该模式支持运行时切换行为(如邮件→短信),并简化单元测试中模拟对象的注入。

优势 说明
可测试性 可注入模拟实现进行隔离测试
可扩展性 新增通知方式无需修改使用者
可维护性 职责清晰,变更影响范围受限
graph TD
    A[UserService] -->|依赖| B[Notifier]
    B --> C[EmailService]
    B --> D[SMSservice]

该架构下,业务逻辑与基础设施解耦,符合依赖倒置原则。

2.2 使用testify/mock时的常见配置错误

错误的期望调用顺序配置

在使用 testify/mock 时,开发者常忽略方法调用顺序的严格性。默认情况下,mock 不强制顺序,但一旦启用 AssertExpectations 而未正确设置调用顺序,测试将意外失败。

mock.On("Save", user).Return(nil).Once()
mock.On("Notify", email).Return(nil).Once()

上述代码未声明调用顺序,若实际调用顺序与注册顺序不同,测试仍会通过,除非显式使用 Call.Unset() 或依赖外部断言。正确做法是结合 mock.ExpectedCalls 手动校验顺序,或在测试逻辑中明确依赖顺序一致性。

返回值类型不匹配

另一个常见问题是返回值类型与接口定义不符:

接口定义返回 Mock 设置返回 是否报错
error nil
*User nil 是(类型不匹配)
bool true

忘记调用 Finish()

部分开发者未在 defer mock.AssertExpectations(t) 后清理预期,导致预期累积。应始终在测试结束时触发断言,确保所有预期被验证,避免误报。

2.3 mock对象生命周期管理不当引发的问题

在单元测试中,mock对象的生命周期若未与测试用例精准对齐,极易导致状态污染与断言失效。尤其在并发或共享测试环境中,问题更为显著。

资源残留与状态泄露

当mock对象未在测试结束后及时释放,可能保留全局状态,影响后续测试执行结果。例如:

import unittest
from unittest.mock import Mock

mock_service = Mock()
mock_service.get_data.return_value = "cached"

class TestBusinessLogic(unittest.TestCase):
    def test_fetch(self):
        result = process(mock_service)
        assert result == "processed: cached"

上述代码中,mock_service为模块级mock,其返回值被固化。若其他测试用例依赖不同行为,将因状态共享而失败。正确做法是将mock置于setUp/tearDown中,确保每次测试前重置。

生命周期管理建议

  • 使用上下文管理器或装饰器自动销毁mock;
  • 避免跨测试用例共享可变mock实例;
  • 利用patch上下文确保作用域隔离。
管理方式 是否推荐 适用场景
模块级mock 无状态、只读依赖
方法内创建 单次测试专用
setUp/tearDown ✅✅ 多方法共享且需重置

自动化清理机制

通过addCleanup注册销毁逻辑,保障生命周期终结:

def setUp(self):
    self.mock_db = Mock()
    self.addCleanup(self.mock_db.reset_mock)

该模式确保无论测试成败,mock都能恢复初始状态,防止副作用传播。

2.4 过度mock导致测试脆弱与维护成本上升

脆弱的测试源于不真实的模拟

当单元测试中对过多外部依赖进行 mock,尤其是深层嵌套对象或间接服务调用时,测试虽能通过,却丧失了对真实协作逻辑的验证。一旦实际接口行为微调,即便功能正常,测试仍可能大面积失败。

维护成本随mock膨胀而上升

以下是一个典型的过度mock示例:

jest.mock('axios');
jest.mock('../services/userService', () => ({
  fetchProfile: jest.fn().mockResolvedValue({ id: 1, name: 'Test User' }),
  updateSettings: jest.fn().mockResolvedValue({ success: true })
}));

上述代码对 userService 的每个方法进行静态mock,导致测试与实现强耦合。若接口返回结构变更,即使业务逻辑无误,测试也将中断。

合理使用mock的策略对比

策略 优点 风险
全量mock 执行快,隔离性好 与真实行为脱节
部分mock + 集成测试 更贴近生产环境 运行较慢
使用真实轻量实现(如内存数据库) 行为准确 搭建复杂

推荐实践路径

graph TD
    A[识别核心依赖] --> B{是否影响执行稳定性?}
    B -->|是| C[进行mock]
    B -->|否| D[使用真实实例或集成测试]
    C --> E[限制mock粒度至接口层]
    D --> F[提升测试可信度]

应优先 mock 不可控外部服务(如第三方API),而对内部模块采用真实调用,降低测试“虚假成功”概率。

2.5 并发测试中mock状态共享引发的数据竞争

在并发单元测试中,多个 goroutine 若共享同一 mock 对象且未加同步控制,极易引发数据竞争。典型表现为 mock 的调用计数、返回值状态被并发修改,导致测试结果非预期。

共享状态的隐患

假设使用 *mock.UserRepository 在多个 goroutine 中模拟数据库操作:

func TestUserService_UpdateConcurrently(t *testing.T) {
    mockRepo := new(mock.UserRepository)
    mockRepo.On("Find", 1).Return(User{Name: "Alice"}, nil)

    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            _, _ = service.UpdateUser(1, "Bob")
        }()
    }
    wg.Wait()
}

上述代码中,mockRepo 被多个 goroutine 同时调用,其内部调用记录和期望匹配机制并非线程安全,可能触发 data race

解决方案对比

方案 是否推荐 说明
使用互斥锁保护 mock ⚠️ 不推荐 破坏 mock 行为自然性
每个 goroutine 使用独立 mock 实例 ✅ 推荐 避免状态共享
使用 channel 统一协调调用 ✅ 推荐 适用于复杂场景

正确做法示意

通过隔离 mock 实例避免共享:

// 每个协程构造独立服务实例与 mock
go func() {
    localMock := new(mock.UserRepository)
    localMock.On("Find", 1).Return(User{}, nil)
    svc := NewUserService(localMock)
    svc.UpdateUser(1, "Charlie")
}()

数据同步机制

可借助 sync.Onceatomic 控制初始化顺序,但更佳实践是设计无状态测试逻辑,从根本上消除竞态条件。

第三章:典型误用场景深度剖析

3.1 场景一:对非接口类型进行强制mock的危害

在单元测试中,开发者常试图对非接口类型(如具体结构体或类)进行强制mock,以隔离外部依赖。这种做法看似简化了测试流程,实则埋下诸多隐患。

破坏封装与耦合加剧

直接 mock 具体实现类型会导致测试代码深度依赖其内部细节。一旦方法签名或调用逻辑变更,所有相关 mock 代码均需同步修改,显著提升维护成本。

运行时行为失真

以下 Go 示例展示了强制 mock 数据库连接的后果:

type MySQLClient struct{}
func (m *MySQLClient) Query(sql string) []byte { /* 实际数据库调用 */ }

// 测试中使用 monkey patch 强制替换
patch := monkey.PatchInstanceMethod(reflect.TypeOf(&MySQLClient{}), "Query", func(_ *MySQLClient, _ string) []byte {
    return []byte(`{"mock": "data"}`)
})

该方式通过运行时函数替换实现 mock,绕过了编译期检查,极易引发 panic 或返回不符合预期的数据结构。

风险类型 后果描述
类型安全丧失 编译器无法检测方法签名变更
并发不安全 monkey patch 在并发测试中可能失效
调试困难 错误堆栈指向被篡改的代码路径

推荐实践路径

应优先依赖依赖倒置原则,通过定义接口抽象行为,再对接口实现 mock。例如引入 DataFetcher 接口,由测试注入模拟实例,从而避免侵入式操作。

3.2 场景二:忽略mock行为定义导致的断言失效

在单元测试中,若仅创建 mock 对象却未定义其行为,可能导致断言失效。例如,mock 方法默认返回 null 或基本类型的默认值,使测试绕过真实逻辑。

常见问题表现

  • 断言始终通过,因返回值为空或 false
  • 实际业务逻辑未被执行,测试失去意义

示例代码

@Test
public void testUserNotFound() {
    UserService mockService = mock(UserService.class);
    // 错误:未定义 when().thenReturn()
    UserController controller = new UserController(mockService);
    User result = controller.getUser("unknown");
    assertNull(result); // 表面正确,实则未测试逻辑
}

上述代码中,mockService.getUser() 未定义返回值,mock 默认返回 null,导致 resultnull。断言通过并非因逻辑正确,而是因 mock 未配置。

正确做法

必须显式定义 mock 行为:

when(mockService.getUser("unknown")).thenReturn(null);

确保测试反映预期逻辑路径,而非 mock 默认行为。

3.3 场景三:在集成测试中滥用mock破坏测试真实性

真实性与隔离性的权衡

集成测试的核心目标是验证组件间的协作是否符合预期。过度使用 mock 会割裂系统依赖,导致测试通过但生产环境故障频发。

典型问题示例

@patch('requests.post')
def test_payment_flow(mock_post):
    mock_post.return_value.status_code = 200
    result = process_payment(100)
    assert result is True

该代码完全模拟了 HTTP 响应,未触及真实网络栈与第三方服务契约。一旦实际接口变更,测试仍通过,造成误判。

逻辑分析mock_post 替换了 requests.post,虽提升执行速度,却丧失对网络延迟、认证机制、JSON 解析等关键路径的验证能力。

推荐实践对比

测试方式 真实性 执行速度 维护成本
全量 mock
使用测试沙箱

改进方向

采用 contract testing 或部署轻量级 stub 服务,如通过 Docker 启动模拟网关,保留通信协议完整性。

graph TD
    A[测试用例] --> B{调用外部API?}
    B -->|Mock| C[返回静态数据]
    B -->|Stub| D[返回动态响应]
    C --> E[通过但失真]
    D --> F[更贴近真实场景]

第四章:正确使用mock的最佳实践

4.1 基于接口设计可测试代码以支持合理mock

在单元测试中,依赖外部服务或复杂组件的代码往往难以隔离测试。通过面向接口编程,可以将具体实现解耦,便于在测试中使用 mock 对象替代真实依赖。

使用接口提升可测试性

定义清晰的接口能有效抽象行为契约。例如:

type UserRepository interface {
    GetUser(id int) (*User, error)
    SaveUser(user *User) error
}

该接口封装了用户数据访问逻辑,业务服务依赖此接口而非具体实现。

在测试中,可实现一个 mock 版本:

type MockUserRepository struct {
    users map[int]*User
}

func (m *MockUserRepository) GetUser(id int) (*User, error) {
    user, exists := m.users[id]
    if !exists {
        return nil, fmt.Errorf("user not found")
    }
    return user, nil
}

通过注入 MockUserRepository,测试无需数据库即可验证业务逻辑。

优势 说明
隔离性 测试不依赖外部系统
可控性 可模拟异常、边界情况
快速执行 避免网络和IO开销

依赖注入促进mock

使用构造函数注入接口实例,使运行时可替换实现:

type UserService struct {
    repo UserRepository
}

func NewUserService(repo UserRepository) *UserService {
    return &UserService{repo: repo}
}

测试时传入 mock,生产环境传入真实实现,实现灵活切换。

4.2 使用gomock进行类型安全的mock生成与验证

在 Go 语言的单元测试中,gomock 提供了类型安全的接口模拟能力,有效提升测试可靠性。通过 mockgen 工具自动生成 mock 代码,开发者可专注于行为验证。

生成 Mock 代码

使用如下命令生成 mock 实现:

mockgen -source=repository.go -destination=mocks/repository.go

该命令解析 repository.go 中的接口,自动生成符合签名的 mock 类型,确保编译期类型检查。

在测试中使用 Mock

ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockRepo := mocks.NewMockRepository(ctrl)
mockRepo.EXPECT().GetUser(1).Return(&User{Name: "Alice"}, nil)

EXPECT() 方法用于声明预期调用,参数匹配和返回值由类型系统保障,避免运行时错误。

验证调用行为

方法 说明
Times(n) 指定期望调用次数
AnyTimes() 允许任意次调用
Do(func) 执行副作用函数

调用流程示意

graph TD
    A[测试开始] --> B[创建gomock控制器]
    B --> C[生成mock对象]
    C --> D[设置方法期望]
    D --> E[执行被测逻辑]
    E --> F[自动验证调用]

4.3 结合真实依赖与mock实现平衡的测试策略

在复杂系统中,完全依赖真实服务或纯 mock 都可能导致测试失真或维护成本上升。理想的策略是根据依赖的稳定性、性能开销和测试目标进行权衡。

混合使用策略

  • 高稳定性外部服务:如时间服务、配置中心,可使用真实实例;
  • 易变或高延迟依赖:如第三方API、数据库写入,应 mock 关键路径;
  • 核心业务逻辑:保留真实依赖以验证集成行为。
# 示例:部分mock数据库,保留消息队列真实连接
@patch('service.db_client.query')
def test_order_processing(mock_query):
    mock_query.return_value = {'status': 'active'}
    result = process_order(123)
    assert result['status'] == 'processed'
    # 真实MQ会接收消息,用于验证事件发布

该测试中,数据库查询被 mock 以隔离外部状态,但消息队列使用沙箱环境真实发送,确保事件驱动链路正确。

决策参考表

依赖类型 是否使用真实实例 原因
本地缓存 快速且可控
第三方支付API 不稳定、有调用成本
内部微服务 视情况 可通过 contract test 验证

测试金字塔中的定位

graph TD
    A[单元测试 - 全mock] --> B[集成测试 - 混合模式]
    B --> C[端到端测试 - 尽量真实]

4.4 清理mock状态确保测试用例间隔离性

在单元测试中,mock对象若未及时清理,可能导致测试用例间状态污染,引发非预期的耦合行为。为保障测试的独立性与可重复性,必须在每个测试执行后重置mock状态。

使用 afterEach 清理 mock

afterEach(() => {
  jest.clearAllMocks(); // 清除所有 mock 的调用记录
  jest.resetAllMocks(); // 重置 mock 的实现为默认行为
});
  • clearAllMocks:仅清除 mock.callsmock.instances 等调用历史,不影响 mock 实现;
  • resetAllMocks:不仅清除调用记录,还会将 mock 恢复为默认空函数,适用于需重置行为的场景。

不同清理策略对比

方法 清除调用记录 重置实现 适用场景
clearAllMocks 需保留 mock 行为但清除历史
resetAllMocks 完全隔离,避免副作用

执行流程示意

graph TD
    A[开始测试用例] --> B[创建 mock]
    B --> C[执行测试逻辑]
    C --> D[验证断言]
    D --> E[清理 mock 状态]
    E --> F[下一个测试用例]

合理使用清理机制,可确保各测试用例运行在纯净的环境中,提升测试可靠性。

第五章:总结与展望

在现代企业级应用架构演进过程中,微服务与云原生技术的深度融合已成为主流趋势。以某大型电商平台的实际落地案例为例,其核心交易系统从单体架构迁移至基于 Kubernetes 的微服务集群后,系统吞吐量提升了约 3.8 倍,平均响应时间从 420ms 下降至 110ms。这一成果并非一蹴而就,而是经历了多个阶段的技术迭代与治理优化。

架构演进路径

该平台采用渐进式重构策略,首先将订单、支付、库存等核心模块拆分为独立服务,并通过 API 网关统一接入。服务间通信采用 gRPC 协议,结合 Protocol Buffers 实现高效序列化。以下为关键服务的部署规模变化:

阶段 服务数量 日均调用量(亿) P99延迟(ms)
单体架构 1 6.2 680
初期拆分 7 8.5 320
完整微服务 15 14.3 110

在此基础上,引入 Istio 服务网格实现流量管理、熔断降级和可观测性增强。通过 VirtualService 配置灰度发布规则,新版本上线失败率下降至 0.3% 以下。

持续交付体系构建

自动化流水线成为保障高频发布的基石。CI/CD 流程整合了代码扫描、单元测试、镜像构建、安全检测与蓝绿部署。以下为典型发布流程的 Mermaid 图表示意:

flowchart LR
    A[代码提交] --> B[静态代码分析]
    B --> C[单元测试 & 集成测试]
    C --> D[构建Docker镜像]
    D --> E[镜像漏洞扫描]
    E --> F[部署到预发环境]
    F --> G[自动化回归测试]
    G --> H[蓝绿切换上线]

每一次代码合并触发的流水线执行平均耗时 8.2 分钟,较传统手动部署效率提升超过 90%。

可观测性实践

面对复杂的服务依赖关系,平台构建了三位一体的监控体系:

  • 日志:基于 ELK 栈集中采集,日均处理日志量达 2.4TB
  • 指标:Prometheus 抓取 1500+ 关键指标,Grafana 看板覆盖业务与系统层
  • 链路追踪:Jaeger 实现全链路跟踪,支持按 trace ID 快速定位性能瓶颈

在一次大促期间,通过链路追踪发现某个缓存穿透问题,团队在 15 分钟内定位并修复,避免了数据库雪崩风险。

未来技术方向

下一代架构规划已启动,重点探索 Service Mesh 数据面性能优化、WASM 插件化扩展以及 AI 驱动的智能弹性调度。初步测试表明,eBPF 结合机器学习模型可将资源预测准确率提升至 92% 以上,为成本优化提供新路径。

记录一位 Gopher 的成长轨迹,从新手到骨干。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注