Creo que la diferencia es el alcance. Si necesitaría el mismo parámetro en todos mis conjuntos de pruebas para, por ejemplo. las variables env, lo usaría en un oyente implementar ITestListener o ISuiteListener (para el ámbito privado) y luego usar el ITestContext/Suitecontext para aplicar el valor a través de todas mis pruebas bajo <test>
o bajo <suite>
Sin embargo, si necesito muy específica params para casos de prueba específicos, luego iría con la anotación @Parameter en esos casos de prueba en particular.
Además, el parámetro @Parameter solo puede tomar valores simples, por lo que puede haber casos de uso en los que las pruebas necesiten objetos diferentes (en lugar de cadenas) en función de algún valor de parámetro. En ese caso, preferiría un proveedor de datos, leyendo el valor param (su segunda opción, aunque argumento para testclass, creo que es incorrecto), crear objetos y luego alimentarlos con las pruebas. (por ejemplo, probablemente el nombre de un archivo de propiedades es un valor de parámetro, que se lee y los objetos se crean en función de los datos en el archivo de propiedades y se alimentan como pruebas diferentes para las pruebas).