Uploading XML test results

dparker's Avatar


27 Sep, 2021 03:00 PM

I am having difficulty doing what should be a simple POST of some test results to appveyor. The build happens on appveyor, and the tests run on a special set of hardware outside of Appveyor. I want to POST the results back to appveyor.

I am following the instructions here:

I have tried 3 approaches:
1) curl
2) POST with node's deprecreated `request` module
3) POST with node-fetch (which is a whatwg-fetch implementation)

In each case I am unable to see the test results in appveyor. And the second two cases yield different status codes for their responses. (404 and 415 respectively).

I am using "Content-Type: multipart/form-data" and wondering if the appveyor server that handles the form-data correctly?

Also note that for the 3rd approach (node-fetch), I have tried 3 implementations for nodejs and get the same results (form-data, formdata-polyfill and formdata-node). I tried a few implementations because I have read some server/clients have differences on how they interpret/implement the form-data spec.

Are there known issues POSTing from different clients?

Below are some simplified examples of the code I have tried that fails.

$ curl --verbose -F 'file={FILEPATH}' -H "Authorization: Bearer {TOKEN}" "https://ci.appveyor.com/api/testresults/junit/{JOBID}"
#No errors with curl... but if I look in appveyor, I see "The build job has not produced any test results."

import request from 'request';
import { createReadStream } from 'graceful-fs';
import fetch from 'node-fetch'; //Using version 2.6.2 because it is CommonJS, whereas 3.x is ES module
import FormData from 'form-data';
import * as path from 'path';

//Example with `request` module (which is deprecated)
const appveyorUploadWithRequest = async (filePath: string, jobId: string, token: string) => {
  const url = `https://ci.appveyor.com/api/testresults/junit/${jobId}`;
  const headers = {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'multipart/form-data'
  await new Promise((resolve, reject) => {
        formData: {
          file: createReadStream(filePath)
      (error, response, body) => {
        if (error != null) {
        const bodyAsString = body.toString();
        if (response.statusCode >= 200 && response.statusCode < 300) {
        } else {
          reject(new Error(`POST ${url}, file: ${filePath}, failed: status ${response.statusCode}: ${bodyAsString}`));

//Example with node-fetch
const appveyorUploadWithFetch = async (filePath: string, jobId: string, token: string) => {
  const url = `https://ci.appveyor.com/api/testresults/junit/${jobId}`;
  const formdata = new FormData();
  formdata.append('file', createReadStream(filePath), path.basename(filePath));
  const headers: HeadersInit = {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'multipart/form-data'
   //Also tried adding `...formdata.getHeaders()` which includes boundary
  const response = await fetch(url, {
    method: 'POST',
    body: formdata as any,
    headers: headers
  if (response.ok) {
    return response.json();
  } else {
    throw new Error(`POST ${url}, file: ${filePath}, failed: status ${response.status}: ${response.statusText}`);

(async () => {
  await appveyorUploadWithRequest('/tmp/result.xml', 'JOBID', 'TOKEN');
  //Throws 404: Job not found or access denied

(async () => {
  await appveyorUploadWithFetch('/tmp/result.xml', 'JOBID', 'TOKEN');
  //Throws status 415.

  1. Support Staff 1 Posted by Feodor Fitsner on 27 Sep, 2021 05:42 PM

    Feodor Fitsner's Avatar

    Sorry for the hassle! You are probably uploading XML results to a finished build? Currently, test results can be uploaded to a runnning build job only.

  2. 2 Posted by dparker on 27 Sep, 2021 06:26 PM

    dparker's Avatar

    Yes - that's what we're doing. It is a finished build. If you do support uploading test results to a job that is finished running, please let me know.

    Our workflow is roughly:
    * Build on appveyor
    * Run tests on our special hardware. (It polls appveyor for new builds)
    * When the tests finish on our special hardware, we want to upload the test results

    I think others building for IoT hardware will want to run their tests on physical hardware like us too. So being able to upload results for tests that can't run in appveyor is useful.

    As a workaround we are capturing our test results in another tool. But we'd like to be able to do so on appveyor one day too.

Reply to this discussion

Internal reply

Formatting help / Preview (switch to plain text) No formatting (switch to Markdown)

Attaching KB article:


Attached Files

You can attach files up to 10MB

If you don't have an account yet, we need to confirm you're human and not a machine trying to post spam.

Keyboard shortcuts


? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac