Je dois dire qu'il n'existe pas de documentation à ce sujet (ou du moins je ne l'ai pas trouvée). Voici donc comment obtenir un fichier JMX valide !

La première étape consiste à initialiser JMeter :

JMeterUtils.setLocale(Locale.ENGLISH);
JMeterUtils.setJMeterHome(jmeterHome);
JMeterUtils.getProperties(jmeterPropFile); //jmeterHome+"/bin/jmeter.properties"

Ensuite, il faut créer l'arbre du plan de test. Cela se fait assez simplement via la ligne de code suivante :

JMeterTreeModel testPlanModel = new JMeterTreeModel();

Afin de pouvoir ajouter des éléments, il faut récupérer le noeud principal du plan de test. Comme cela n'est documenté nulle part, je l'ai tiré directement du fichier source de JMeterTreeModel :

JMeterTreeNode rootNode = (JMeterTreeNode)testPlanModel.getRoot();
JMeterTreeNode testPlanNode = (JMeterTreeNode)rootNode.getChildAt(0);

Maintenant il suffit d'ajouter les éléments désirés pour créer l'arbre de test. Un petit bémol toutefois : il est nécessaire de passer par la classe GUI de chacun des TestElement afin que les propriétés soient générées correctement...

ThreadGroupGui gui = new ThreadGroupGui();
org.apache.jmeter.threads.ThreadGroup threadGroup = (org.apache.jmeter.threads.ThreadGroup)gui.createTestElement();
 
//ajout de l'élément en précisant le noeud parent
testPlanModel.addComponent(threadGroup, testPlanNode);

Vient ensuite la sauvegarde du fichier JMX. La aussi, j'ai du fouiller dans le code source pour finalement extirper la méthode ci-dessous de la classe Save :

private void convertSubTree(HashTree tree) 
{
    Iterator iter = new LinkedList(tree.list()).iterator();
    while (iter.hasNext()) 
    {
        JMeterTreeNode item = (JMeterTreeNode) iter.next();
        convertSubTree(tree.getTree(item));
        TestElement testElement = item.getTestElement();
        tree.replace(item, testElement);
    }
}

Et pour terminer, voici le morceau de code qui permet d'écrire le fichier qui sera lu par JMeter :

HashTree tree = testPlanModel.getTestPlan();
convertSubTree(tree);
 
FileOutputStream stream = new FileOutputStream(jmxFileName);
SaveService.saveTree(tree, stream);
stream.close();

Pour les intéressés, j'ai mis en annexe une partie de la classe que j'ai utilisée pour générer mes tests.