As @pranavs stated, what occurs in Juniper products, and most all other products is based on flows, not packets. Plus, no one really does load-balancing, it is really load-sharing. Based upon randomness of MAC/IP address/UDP or TCP port numbers, the idea is to get a close to equal load-balancing as possible. Load-balancing can only occur with per-packet algorthims, but per packet are generally not used as out-of-sequence could occur. This is why almost everyone uses flow-based algorthims, not packet based.
So if in your test you only use 1 flow (same MAC/IP SA and DA, etc.) then no load-sharing should occur. For real test results you should use 100s or better yet 1000s of different flows. Depending upon Juniper product, there maybe options for different hash algorthims to determine how flows are distrubted across an AE/LAG. For any one product, the determination should be the same, if configured the same. What does this all lead to? For MX to MX one should expect (with same config) any specific flow to be always associated with the same link within the AE/LAG bundle always. For connection between 2 different products, in your case EX/QFX [not sure which exact models] there is the potential for different algorthims to be in use, and therefore by default one might send on link A, while the other sends the same reverse flow, down link B.
This could well explain the behavior you saw as very normal. The reason for more advanced and configurable hash algorthims for how AE/LAG links are associated with any flow, is due to fact that even with randomness of flows, all flows could actually choose the same link, and therefore no other links within the AE/LAG would be used.
Hopefully this may help you, and/or others.