How to Interact with Files and with the Environment in Test Scripts
In addition to the test-specific functionality that Squish provides, test scripts can also use the native functionality (including the standard libraries) provided by the scripting languages themselves. In this subsection we will show how to use native functionality to read data from an external file, write data to an external file, check for the existence of an external file, and delete an external file. In addition, we will see how to compare two external files, and also how to read the environment variables that are set when the script is running.
Python-specific
The Python examples don't show any import
statements, but these are of course required when non-global functions are used. The import
statements ought to be done before the main
function is defined, with those shown below being sufficient for the examples shown in this section.
import codecs, filecmp, os, subprocess, sys
Perl-specific
The Perl examples don't show any use
statements, but these are of course required when non-global functions are used. The use
statements ought to be done before the main
function is defined, with those shown below being sufficient for the examples shown in this section.
use File::Basename; use File::Spec;
How to Interact with External Files in Test Scripts
Here we will see how to read data from an external file, write data to an external file, check for the existence of an external file, and delete an external file.
How to Read Data from an External File
Reading an external file involves getting its complete filename (including path), and then reading it in the standard way that the scripting language supports. For example:
infile = findFile("testdata", "before.csv") infile = infile.replace("/", os.sep) test.log("Reading %s" % infile) file = codecs.open(infile, "r", "utf-8") lines = [] for line in file: lines.append(line) file.close() test.verify(len(lines) == 13)
infile = findFile("testdata", "before.csv"); infile = infile.replace(/[\/]/g, File.separator); test.log("Reading " + infile); file = File.open(infile, "r"); var lines = []; var i = 0; while (true) { var line = file.readln(); if (line == null) break; lines[i++] = line; } file.close(); test.verify(lines.length == 13);
my $sep = File::Spec->rootdir(); # Load data from an external file my $infile = findFile("testdata", "before.csv"); $infile =~ s,/,$sep,g; test::log("Reading $infile"); open(FILE, "<:encoding(UTF-8)", $infile) or test::fail("Failed to read $infile"); my @lines = <FILE>; close(FILE); test::verify(scalar(@lines) == 13);
infile = natify(findFile("testdata", "before.csv")) Test.log("Reading %s" % infile) lines = [] File.open(infile, "r:utf-8") do |file| file.each {|line| lines << line} end Test.verify(lines.length == 13)
set infile [file nativename [findFile "testdata" "before.csv"]] test log "Reading $infile" set fh [open $infile] set text [read $fh] close $fh set text [string trimright $text] set lines [split $text "\n"] test compare [llength $lines] 13
Here, we read a file called before.csv
that is in the suite's (or the test case's) testdata
directory. The file is a text file using the UTF-8 encoding. We open the file and read it line by line into a list (or array) of lines or into a string which we then break into lines, depending on the scripting language. And at the end we check that we have got exactly the number of lines we expected.
Squish uses Unix-style path separators internally on all platforms, but because we want to show the path to the user (using the test.log(message) function), we replace these with the path separator that is appropriate for the platform (e.g., "\
" on Windows).
JavaScript has no native support for file handling or for operating system interaction, so Squish provides the File Object and the OS Object to fill these gaps.
How to Write Data to an External File
Writing to an external file is simply a matter of creating a filename, opening the file for writing, and writing data to it in the standard way that the scripting language supports. For example:
outfile = os.path.join(os.getcwd(), os.path.basename(infile) + ".tmp") outfile = outfile.replace("/", os.sep) test.log("Writing %s" % outfile) file = codecs.open(outfile, "w", "utf-8") for line in lines: file.write(line) file.close()
outfile = infile + ".tmp"; var i = outfile.lastIndexOf(File.separator); if (i > -1) outfile = outfile.substr(i + 1); outfile = OS.cwd().replace(/[\/]/g, File.separator) + File.separator + outfile; test.log("Writing " + outfile); file = File.open(outfile, "w") for (var i in lines) file.write(lines[i] + "\n"); file.close();
$outfile =~ s,/,$sep,g; test::log("Writing $outfile"); open(FILE, ">:encoding(UTF-8)", $outfile) or test::fail("Failed to write $outfile"); print FILE @lines; close(FILE);
outfile = natify(File.join(Dir.getwd, File.basename(infile) + ".tmp")) Test.log("Writing %s" % outfile) File.open(outfile, "w:utf-8") do |file| lines.each {|line| file.write(line)} end
set outfile [file nativename [file join [pwd] [file tail "$infile.tmp"]]] test log "Writing $outfile" set fh [open $outfile "w"] foreach line $lines { puts $fh $line } close $fh
Here, we write a file that has the same basename as the file we read, but with .tmp
appended (e.g., before.csv.tmp
), and save it into the script's current working directory. Since we write exactly the same data as we read, this file and the original should be identical. (We'll see how to check this in a later subsection.)
Just as we did when reading a file, we replace the Unix-style path separators with the path separator that is appropriate for the platform. This is done purely for the output to the test.log(message) function; if we are not showing the filename to the user we could safely use Unix path separators no matter what the platform.
How to Check the Existence of an External File
Here is an example that checks two files: the first is expected to exist and the second is not expected to exist.
test.verify(os.path.exists(infile), "infile correctly present") test.verify(not os.path.exists(outfile), "outfile sucessfully deleted")
test.verify(File.exists(infile), "infile correctly present"); test.verify(!File.exists(outfile), "outfile sucessfully deleted");
test::verify(-e $infile, "infile correctly present"); test::verify(!-e $outfile, "outfile sucessfully deleted");
Test.verify(File.exist?(infile), "infile correctly present") Test.verify(!File.exist?(outfile), "outfile sucessfully deleted")
test verify [file exists $infile] "infile correctly present" test verify [expr ![file exists $outfile]] "outfile sucessfully deleted"
We have used the two-argument form of the Boolean test.verify(condition) function to provide more useful detail information than simply "True expression".
How to Remove an External File
Removing an external file is easy, but not reversible.
os.remove(outfile)
File.remove(outfile);
unlink $outfile;
File.delete(outfile)
file delete $outfile
Follow this with a call to the Boolean test.verify(condition) function in conjunction with an existence test to check that the file has been removed as expected.
How to Compare External Files inside Test Scripts
To compare two external files, the easiest approach is to use the Comparing Files Squish API, but it is also possible to use the native script language features. It should not be necessary to use an external program like diff
or fc
.
# Compare using Squish API: test.compareTextFiles(infile, outfile) # Compare using Python API: test.verify(filecmp.cmp(infile, outfile, False), "infile and outfile equal according to filecmp library")
// Compare two external files: test.compareTextFiles(infile, outfile)
# Compare two external files test::compareTextFiles($infile, $outfile);
# Compare two external files Test.compareTextFiles(infile, outfile)
# Compare two external files test compareTextFiles $infile $outfile
We have to make our script account for the fact that we use the fc program on Windows and the diff program on other platforms. Fortunately, both programs exhibit the same behavior: if the two files are the same they return 0 to the operating system (0 is the traditional "success" value for programs) and if the two files differ they return 1. (They may return other values, e.g., 2, if an error in the command line they are given is encountered.)
Note: It is essential that the filenames use the correct path separators for the platform. We also put the filenames in quotes, except for the Tcl example, in case they or their paths contain spaces, which is usual on Windows.
Python-specific
Python programmers can avoid using an external program and also the inefficiency of loading entire files into memory by taking advantage of the Python standard library's filecmp
module. This reduces comparing two files to a single statement:
test.verify(filecmp.cmp(infile, outfile, False), "infile and outfile equal according to filecmp library")
The filecmp.cmp function returns a Boolean with True
indicating that the files are the same. The third parameter should always be False
to avoid any risk of false-positives.
How to Read Environment Variables inside Test Scripts
Here is an example of how to read some specific environment variables that might be set when the test script is running.
for key in ("HOME", "PATH", "MY_ENV_VAR"): test.log("%s = %s" % (key, os.environ.get(key)))
var keys = ["HOME", "PATH", "MY_ENV_VAR"]; for (i in keys) test.log(keys[i] + " = " + OS.getenv(keys[i]));
for my $key ("HOME", "PATH", "MY_ENV_VAR") { test::log("$key = $ENV{$key}"); }
ENV.each {|key, value| Test.log("#{key} = #{value}") }
global env foreach key {"HOME" "PATH" "MY_ENV_VAR"} { set value "" if {[catch {set value $env($key)}]} { # do nothing for missing key: empty default value is fine } test log "$key = $value" }
If you use a shell script or batch file as your AUT you can easily set some test-suite-specific environment variables which you can then access inside your test scripts using the technique shown here. See Shell Scripts and .bat-Files as AUT.) Test scripts do not have access to the AUT's environment variables, such as the ones set in the Test Suite Settings view's Environment section.
For Python, using the dict.get
method ensures that we get None
as the value of missing keys rather than an exception. Similarly, for Tcl we achieve the same thing by catching the exception that occurs if the looked up key is missing. For JavaScript and Perl, a missing key's value is a harmless empty value which prints as an empty string.
© 2024 The Qt Company Ltd.
Documentation contributions included herein are the copyrights of
their respective owners.
The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation.
Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property
of their respective owners.