Download File Testing

So this is for downloading file testing.

First of all why do you want to download the file? Are you going to do anything with it?
The majority of people who want to download files just do it so that they can show an automation framework downloading files because it makes somebody non-technical ooo and ahh.
You can check the header response to check that you get a 200 OK (or maybe a redirect, depends on your expected outcome) and it will tell you that a file exists.
Only download files if you are actually going to do something with them, if you are downloading them for the sake of doing it you are wasting test time, network bandwidth and disk space.

The best way I have found to do this is by accessing the page, getting the download link, and performing a HEAD request for the file with an HTTP library. The response will contain the length of the file and its type.
A HEAD request is preferable since it will only retrieve the headers instead of pulling down the entire file.
And if the file is behind auth, you will need to pull the session cookie from Selenium’s cookie store and pass it into the HTTP library when performing the request.
That, or you can configure the browser you’re using to auto-download files to a specific location and then perform checks against the file on disk.

If you follow that path, using REST helper to send request and get the file, headers, etc.

In this following example, I will show you a way to get the file downloaded and check its length and type.

First of all, your config:

exports.config = {
  tests: './tests/**/*.js',
  timeout: 10000,
  output: './output',
  helpers: {
    Webdriver: {
      url: process.env.URL,
      browser: 'chrome',
      desiredCapabilities: {
        chromeOptions: {
          args: ['--headless', '--disable-gpu', '--start-fullscreen'],
        },
      },
      keepCookies: false,
      restart: true,
      windowSize: '1440x700',
    },
    REST: {
      endpoint: 'https://sample-videos.com/text',
    },
    FileSystem: {

    }
  },

Next, your test scenarios:

import { expect } from 'chai';
Feature('Download File');

Scenario('Download file @test', async (I) => {
  const res = await I.sendGetRequest('/Sample-text-file-10kb.txt');
  I.writeToFile('test.txt', res.data);
  I.seeFile('test.txt');
});

Scenario('Verify file length and type @test', async (I) => {
  const res = await I.sendGetRequest('/Sample-text-file-10kb.txt');
  expect(res.headers['content-length']).to.equal('9510');
  expect(res.headers['content-type']).to.equal('text/plain');
});

Execute the tests:

C02XR2AGJHD2:tests thanh.nguyen$ ./node_modules/.bin/codeceptjs run --grep @test
2019-03-14T09:40:32.836Z DEBUG wdio-config: @wdio/sync not found, running tests asynchronous
CodeceptJS v2.0.4
Using test root "/Users/thanh.nguyen/Desktop/tests"

Download File --
  ✔ Download file @test in 631ms
  ✔ Verify file length and type @test in 658ms

  OK  | 2 passed   // 4s
1 Like

I would like to mention so if you want to make REST API requests within a browser session you should share cookies between REST API and WebDriver helpers, as it is mentioned in this chapter:

Based on this, i created a helper to handle my download tests.
My helper:

'use strict';

const {expect} = require('chai')
const Helper = codecept_helper;

class DownloadFile extends Helper {

    async checkDownloadUrl(url,file) {
        const rest = this.helpers['REST']

        let res = await rest.sendGetRequest(url)
        
        let {   "content-length": contentLength ,
                "content-type": contentType } = res.headers
                
        contentLength = parseInt(contentLength)
    
        expect(res.status).to.equal(200)
        expect(contentLength).to.be.closeTo(file.size,10)
        expect(contentType).to.equal(file.type)
    }

}
module.exports = DownloadFile;

My scenario:

Scenario('Download some file', async (I)=>{
    
    let url = await I.grabAttributeFrom(buttons.download,'href')

    I.checkDownloadUrl(url[0],{size:31484,type:'application/zip'}) 
  
})

Download file --
  ✔ Download some file in 4026ms

  OK  | 1 passed   // 8s

On fail:

Download file --
  ✖ Download some file in 4027ms

-- FAILURES:

  1) Download file
       Download some file:
     expected 31485 to be close to 31454 +/- 10

Theres a lot more that we can do with it :slight_smile:

ps1: Used to be close, cos’ in my app the file size changes depending on the clock time it was downloaded

1 Like

Whats the scenario if your downloading a file by simply clicking on a button. with out any url or any API.