[Class] expect.ahk (rapid unit testing)

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
Chunjee
Posts: 1418
Joined: 18 Apr 2014, 19:05
Contact:

[Class] expect.ahk (rapid unit testing)

Post by Chunjee » 26 Sep 2021, 00:09

Image

Image Image Image Image Image


Installation

In a terminal or command line navigated to your project folder:

Code: Select all

npm install expect.ahk
In your code only export.ahk needs to be included:

Code: Select all

#Include %A_ScriptDir%\node_modules
#Include expect\export.ahk
expect := new expect()

testVar := 2 + 2
expect.equal(testVar, 4)
expect.fullReport()
You may also review or copy the library from ./export.ahk on GitHub; #Include as you would normally when manually downloading.


Usage

Grants access to a class named expect with the following methods: .equal, .notEqual, .true, .false, .label, .group, .report, .fullReport, and .writeResultsToFile

Code: Select all

expect := new expect()

; .equal checks and logs whether or not both arguments are the same
expect.label("string comparison")
expect.equal("StringExample", "StringExample")

expect.label("value testing")
expect.equal((1 > 0 ), true)

expect.label("true/false testing")
expect.true((1 == 1))
expect.false((1 != 1))
expect.notEqual(true,false)

expect.report()
expect.fullReport()
expect.writeResultsToFile()

API

.equal(actual, expected)

Alias: .test

checks if actual and expected are the same or equal. The comparison is case-insensitive when ahk is inStringCaseSense, Off (default ahk behavior)

Arguments
  1. actual (*): The actual value computed
  2. expected (*): The expected value
Returns (boolean): returns true if the values were the same, else false

Example

Code: Select all

expect.equal("string", "tsring")
; => false

expect.equal((1 > 0 ), true)
; => true
.true(actual) checks if actual value is true.

Arguments
  1. actual (*): The actual value computed
Returns (boolean): returns true if the value is true, else false

Example

Code: Select all

expect.true((1 == 1))
; => true

expect.true(InStr("String", "S"))
; => true
.false(actual) checks if actual value is false.

Arguments
  1. actual (*): The actual value computed
Returns (boolean): returns true if the value is false, else false

Example

Code: Select all

expect.false((1 != 1))
; => true

expect.false(InStr("String", "X"))
; => true
.notEqual(actual, expected) checks if actual and expected are NOT the same or equal. The comparison is case-insensitive when ahk is inStringCaseSense, Off (default ahk behavior)

Arguments
  1. actual (*): The actual value computed
  2. expected (*): The expected value
Returns (boolean): returns true if the value is false, else false

Example

Code: Select all

expect.notEqual((1 != 1))
; => true

expect.notEqual(InStr("String", "X"))
; => true
.undefined(actual) checks if actual is undefined ("").

Arguments
  1. actual (*): The actual value computed
Returns (boolean): returns true if the value is "", else false

Example

Code: Select all

expect.false((1 != 1))
; => true

expect.false(InStr("String", "X"))
; => true
.label(label) labels the following tests for logs and readability

Arguments
  1. label (string): A human readable label for the next test(s) in sequence
Example

Code: Select all

expect.label("string comparisons")

expect.equal("String", "s")
expect.fullReport()
/*---------------------------
1 tests completed with 0% success (1 failure)
=================================
== string comparisons ==
Test Number: 1
Expected: s
Actual: String
---------------------------*/
.group(label) appends the label to a group of following tests for logs and readability

This may be useful when one has a lot of tests; and doesn't want to type out a repeatative label

Arguments
  1. label (string): A human readable label prepend for the next test(s) in sequence
Example

Code: Select all

expect.group("strings")
expect.label("comparison")
expect.equal("String", "s")

expect.label("length")
expect.equal(strLen("String"), 9)

expect.fullReport()
/*---------------------------
2 tests completed with 0% success (2 failures)
=================================
== strings - comparisons ==
Test Number: 1
Expected: s
Actual: String

== strings - length ==
Test Number: 2
Expected: 99
Actual: 6
---------------------------*/
.report() Uses msgbox to display the results of all tests

Example

Code: Select all

expect.true(InStr("String", "S"))

expect.report()
/*---------------------------
1 test completed with 100% success
---------------------------*/
.fullReport() Uses msgbox to display the results of all tests with details of any failures

Example

Code: Select all

expect.true(InStr("String", "X"))

expect.fullReport()
/*---------------------------
1 tests completed with 0% success (1 failure)
=================================
Test Number: 1
Expected: true
Actual: false
---------------------------*/
.writeResultsToFile([filepath, fileopen]) writes test results to a file

Arguments
  1. filepath (string): Optional, The file path to write all tests results to, the default is A_ScriptDir "\result.tests.log"
Example

Code: Select all

expect.true(InStr("String", "X"))

expect.writeResultsToFile()
/*
1 test completed with 0% success (1 failure)

Test Number: 1
Expected: true
Actual: false*/
Last edited by Chunjee on 14 Apr 2024, 08:55, edited 5 times in total.


User avatar
Chunjee
Posts: 1418
Joined: 18 Apr 2014, 19:05
Contact:

Re: [Class] expect.ahk (rapid unit testing)

Post by Chunjee » 26 Sep 2021, 00:16

When I decided to start writing biga.ahk; I knew there were going to be a lot of methods and many that feed off each others output. So I needed somekind of way to ensure updates and small changes didn't cause things to break elsewhere. Therefore I wrote this this unit testing class and made a lot of small improvements whenever I encountered a new need. As of this writing that project has 650 tests and most didn't take longer than a few seconds to write.

There are some other unit testing projects already out there that are more conventional; but I was going to have hundreds of tests, the normal way to write tests is quite verbose where you extend the class a lot. The scale of the project I was starting would probably make writing tests that way basically impossible for one person I felt.
This exists to make it easier to write a lot of small tests. But other testing libraries are probably more powerful.

User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: [Class] expect.ahk (rapid unit testing)

Post by kczx3 » 27 Sep 2021, 18:54

Your second code box uses “new unit()”. Did you mean “new expect()”?


User avatar
Chunjee
Posts: 1418
Joined: 18 Apr 2014, 19:05
Contact:

Re: [Class] expect.ahk (rapid unit testing)

Post by Chunjee » 29 Jul 2022, 19:39

I found what I would call a big bug.

fixed in v0.1.1
expect.ahk will always test with case sensitivity on now

DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 12 Dec 2023, 04:25

Hi @Chunjee, great class, thanks for providing.

I've a few Qs, but am new to classes and units testing and only an average amateur AutoHotkey user - so hopefully these Qs are not foolish!:
(I'm using exportv0.1.1.ahk)

1) The example given in the documentation for .writeResultsToFile([filepath, fileopen]) calls for the method .writeTestResultsToFile, but the method name is .writeResultsToFile?

2) In exportv0.1.1.ahk there's an empty method writeTAP(...)?

3) The example given in the documentation for .undefined(actual) uses expect.false(...). Should probably be expect.undefined(...)?

4) There is no undefined method expect.undefined("")?

5) In the method test(...) the last parameter is param_note:="", but this is not used in the method?

6) param_note is a parameter for the following methods: true, false, equal, notEqual, but is only used in the last method.

7) In the method test(...), should the line this._logTestFail(param_actual, param_expected) read this._logTestFail(param_actual, param_expected, param_note)? This allows a 'per-test' message to be added to each test failure.

Cheers, and many thanks again :thumbup:


DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 02 Jan 2024, 04:47

Thanks @Chunjee, looking forward to your feedback when you have time :thumbup:

DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 22 Feb 2024, 07:50

Hi Chunjee, respectfully wondering if you had a moment to look through the Qs I raised above?

Very many thanks :thumbup:

User avatar
Chunjee
Posts: 1418
Joined: 18 Apr 2014, 19:05
Contact:

Re: [Class] expect.ahk (rapid unit testing)

Post by Chunjee » 22 Feb 2024, 11:25

Haven't forgotten. I did confirm some of the issues however. Thank you

DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 23 Feb 2024, 00:02

Chunjee wrote:
22 Feb 2024, 11:25
Haven't forgotten. I did confirm some of the issues however. Thank you
:thumbup: :thumbup:

User avatar
Chunjee
Posts: 1418
Joined: 18 Apr 2014, 19:05
Contact:

Re: [Class] expect.ahk (rapid unit testing)

Post by Chunjee » 07 Mar 2024, 14:40

Github repo contains most fixes. Documentation has not been fixed yet.

I noticed a TAP 14 spec which this may not be following completely. I would like to get that looked at as well

DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 07 Apr 2024, 09:57

Chunjee wrote:
07 Mar 2024, 14:40
Github repo contains most fixes. Documentation has not been fixed yet.

I noticed a TAP 14 spec which this may not be following completely. I would like to get that looked at as well
Many thans for this :thumbup: comments to follow...

DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 07 Apr 2024, 10:14

Hi, sorry it's taken so long to get back to you. Quoting my original post, here are my thoughts (as you point out there are still some documentation fixes, but thought it might be helpful to summarise below):
DaveT1 wrote:
12 Dec 2023, 04:25
1) The example given in the documentation for .writeResultsToFile([filepath, fileopen]) calls for the method .writeTestResultsToFile, but the method name is .writeResultsToFile?
So the code is correct, but the documentation is wrong, ie in the documentation .writeTestResultsToFile should be replaced with .writeResultsToFile.
DaveT1 wrote:
12 Dec 2023, 04:25
2) In exportv0.1.1.ahk there's an empty method writeTAP(...)?
This empty method has been removed from the new code. I guess that it was a placeholder for the TAP spec that you mentioned previously.
DaveT1 wrote:
12 Dec 2023, 04:25
3) The example given in the documentation for .undefined(actual) uses expect.false(...). Should probably be expect.undefined(...)?
Actually now that I look again at this, I think that the example given here is relevant to a .false assertion, not a .undefined check. In fact it looks identical to the example for .false. So probably a simple copy-paste error?
So this means that there is still no proper published example in the documentation for .undefined – but see note in next section.
DaveT1 wrote:
12 Dec 2023, 04:25
4) There is no undefined method expect.undefined("")?
The new code has a new method undefined(param_actual:="_Missing_Parameter_", param_note:="") at line 147.
As noted in (3) there is still no proper published example in the documentation for .undefined.
But in the (new) comments for each method has this: @example expect.undefined("").
DaveT1 wrote:
12 Dec 2023, 04:25
5) In the method test(...) the last parameter is param_note:="", but this is not used in the method?
The new code has corrected this.
DaveT1 wrote:
12 Dec 2023, 04:25
6) param_note is a parameter for the following methods: true, false, equal, notEqual, but is only used in the last method.
Hmm, now I look at this again, I disagree with what I wrote above. param_note is used in each of the above methods. My bad! I suspect I saw the first return and thought that was the end of the method. Sorry :facepalm:
DaveT1 wrote:
12 Dec 2023, 04:25
7) In the method test(...), should the line this._logTestFail(param_actual, param_expected) read this._logTestFail(param_actual, param_expected, param_note)? This allows a 'per-test' message to be added to each test failure.
The new code has corrected this.

So very many thanks for all your effort on this...I have noticed a few other issues, but will mention those in a separate post (probably tomorrow now).

DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 08 Apr 2024, 07:57

Hi Chunjee,

As promised some further observations which I've gone ahead and added these to a copy of export.ahk that I downloaded on 02-Apr-24:

Code: Select all

class expect {
	__New() {
		this.testTotal := 0
		this.failTotal := 0
		this.successTotal := 0
		this.log := []
		this.groupVar := ""
		this.labelVar := ""
		this.lastlabel := ""
		this.finalTrigger := false

        ;;Q8 Added so output can identify what each test was (equal, undefined, etc)
        this.TestType := ""        
	}
	/**
	* checks if param_actual and param_expected inputs are the same or equal. The comparison is always case-sensitive.
	* @param {number|string|object} param_actual - The actual value.
	* @param {number|string|object} param_expected - The expected value.
	* @param {string} param_note - (Optional) Additional notes for the test.
	*
	* @returns {boolean} Returns true if the values are equal, else false.
	*
	* @example expect.equal([1, 2, 3], [1, 2, 3])
	*
	* @alias .test
	* @example
	* ### Example usage
	* You can use like so:
	* ```autohotkey
	* expect.equal(inStr("String", "ing"), 4)
	* ```
	*/
	equal(param_actual:="_Missing_Parameter_", param_expected:="_Missing_Parameter_", param_note:="") {
		if (A_IsCompiled) {
			return 0
		}
		; create
        ;;Q8a
        this.TestType := "equal"
		return this.test(param_actual, param_expected, param_note)
	}
	/**
	* checks if actual and expected inputs are NOT the same or equal. The comparison is always case-sensitive.
	* @param {number|string|object} param_actual - The actual value computed.
	* @param {number|string|object} param_expected - The expected value.
	* @param {string} param_note - Additional notes for the test (Optional).
	*
	* @returns {boolean} Returns true if the values are different, else false.
	*
	* @example expect.notEqual({ "a":1 }, { "a":false })
	*/
	notEqual(param_actual:="_Missing_Parameter_", param_expected:="_Missing_Parameter_", param_note:="") {
		if (A_IsCompiled) {
			return 0
		}
		if (param_note == "" && this.lastlabel == "") {
			param_note := "Expected to be different"
		}
		; prepare
		param_actual := this._print(param_actual)
		param_expected := this._print(param_expected)
		; create
        ;;Q8b
        this.TestType := "notEqual"
		this.testTotal += 1
		if (param_actual != param_expected) {
			this.successTotal++
			return true
		} else {
			this._logTestFail(param_actual, param_expected, param_note)
			return false
		}
	}
	/**
	* checks if actual and expected inputs are the same or equal. The comparison is always case-sensitive.
	* @param {number|string|object} param_actual - The actual value computed.
	* @param {number|string|object} param_expected - The expected value.
	* @param {string} param_note - Additional notes for the test (Optional).
	* @returns {boolean} Returns true if the values are equal, else false.
	*/
	test(param_actual:="_Missing_Parameter_", param_expected:="_Missing_Parameter_", param_note:="") {
		if (A_IsCompiled) {
			return 0
		}
		; prepare
		if (isObject(param_actual)) {
			param_actual := this._print(param_actual)
		}
		if (isObject(param_expected)) {
			param_expected := this._print(param_expected)
		}
		; create
		this.testTotal++
		if (param_actual == param_expected) {
			this._stdOut("ok " this.testTotal)
			this.successTotal++
			return true
		} else {
			this._logTestFail(param_actual, param_expected, param_note)
			return false
		}
	}
	/**
	* checks if actual value is true.
	* @param {number|string} param_actual - The actual value computed.
	* @param {string} param_note - Additional notes for the test (Optional).
	*
	* @returns {boolean} Returns true if the values are different, else false.
	*
	* @example expect.true((1 == 1))
	*/
	true(param_actual:="_Missing_Parameter_", param_note:="") {
		if (A_IsCompiled) {
			return 0
		}
		; create
        ;;Q8c
        this.TestType := "true"
		this.test(param_actual, true, param_note)
		return false
	}
	/**
	* checks if actual input is false.
	* @param {number|string|object} param_actual - The actual value computed.
	* @param {string} param_note - Additional notes for the test (Optional).
	*
	* @returns {boolean} returns true if the value is false, else false
	*
	* @example expect.false((99 < 3))
	*/
	false(param_actual:="_Missing_Parameter_", param_note:="") {
		if (A_IsCompiled) {
			return 0
		}
		; create
        ;;Q8d
        this.TestType := "false"
		if (param_actual == false) {
			this.test("false", "false", param_note)
			return true
		}
		if (param_actual == true){
			this.test("true", "false", param_note)
			return false
		}
		this.test(param_actual, "true", param_note)
		return false
	}
	/**
	* checks if actual is undefined (`""`).
	* @param {number|string} param_actual - The actual value computed.
	* @param {string} param_note - Additional notes for the test (Optional).
	*
	* @returns {boolean} returns true if the value is `""`, else false
	*
	* @example expect.undefined("")
	*/
	undefined(param_actual:="_Missing_Parameter_", param_note:="") {
		if (A_IsCompiled) {
			return 0
		}
		; create
        ;;Q8e
        this.TestType := "undefined"
		this.test(param_actual, "", param_note)
		return false
	}
	/**
	* appends the label to a group of following tests for logs and readability
	* @param {string} param_label - A human readable label prepend for the next test(s) in sequence
	*
	* @example expect.group("Object Tests")
	*/
	group(param_label) {
		if (A_IsCompiled) {
			return 0
		}
		this.groupVar := param_label
		this.labelVar := ""
		this.lastlabel := "_"
		this._stdOut("# " param_label)
		return
	}
	/**
	* labels the following tests for logs and readability
	* @param {string} param_label - A human readable label for the next test(s) in sequence
	* 
	* @example expect.label("myInterestingLabel")
	*/
	label(param_label) {
		if (A_IsCompiled) {
			return 0
		}
		this.labelVar := param_label
		return
	}
	_buildReport() {
		if (A_IsCompiled) {
			return 0
		}
		; create
		this.percentsuccess := floor( ( this.successTotal / this.testTotal ) * 100 )
		returntext := this.testTotal " tests completed with " this.percentsuccess "% success (" this.failTotal " failures)"
		if (this.failTotal = 1) {
			returntext := strReplace(returntext, "failures", "failure")
		}
		if (this.testTotal = 1) {
			returntext := strReplace(returntext, "tests", "test")
		}

        ;;Q2b I've added underlining here and commented out in the 1 other place.
        ;;So the underlining is added to both the messagebox and output to file.
        ;;Add underlining to report top line.
		returntext .= "`n=================================`n"

		return returntext
	}
	_final() {
		if (A_IsCompiled) {
			return 0
		}
		; create
        ;;Q5 Code suggests this produces the 1-line test summary report to stdOut.
        ;;I don't understand why the stdout output needs to have hard coded 
        ;;"1.." or "# "! So I've commented these out, but left the generation
        ;;of the 1-line test summary in.
		;this._stdOut("1.." this.testTotal)
		;this._stdOut("# " this._buildReport())
		this.finalTrigger := true
		return true
	}
	_logTestFail(param_actual, param_expected, param_msg:="") {
		if (A_IsCompiled) {
			return 0
		}
		; create
		this.failTotal++
        ;;Q1 Commenting out the next 3 lines means that the test label 
        ;;text is not replaced with the test note text.
		;;if (param_msg != "") {
		;;	this.labelVar := param_msg
		;;}
		if (this.labelVar != this.lastlabel) {
			this.lastlabel := this.labelVar
			if (this.groupVar) {
				this.log.push("== " this.groupVar " - " this.labelVar " ==")
			} else {
				this.log.push("== " this.labelVar " ==")
			}
		}
        ;;Q6 So if Label is not defined, make marker text for the output.
        ;;so it's visually easier to see each test.
        else
            this.log.push("== Label: ==")
        
        ;;Q7 Done a lot of 'fiddling' to improve the output.
        ;;I've made msg for stdout and a new msg1 for reporting in msgboxs and to file.
        ;;I've included a new var this.TestType - see Q8
        
		;msg := "not ok " this.testTotal " - " this.labelVar "`n"
        msg := "Test " this.testTotal " not ok (""" this.labelVar """)`n"
		msg1 := "This (should be " this.TestType ") test failed`n"

        msg .= "   Should be: " this.TestType "`n"
		
        ;;Q7 I cant see what this adds. So removed to declutter output.
        ;msg .= "    ---`n"

		msg .= "    Expected: " param_expected "`n"
		msg1 .= "    Expected: " param_expected "`n"
		msg .= "    Got: " param_actual "`n"
		msg1 .= "    Got: " param_actual "`n"

		;;Q7 I cant see what this adds. So removed to declutter output.        
		;msg .= "    ..."

        ;;Q7
        if (param_msg != "")
        {
            msg .= "    Note: " param_msg "`n"
            msg1 .= "    Note: " param_msg "`n"
        }

		this._stdOut(msg)
		this.log.push(msg1)
	}
	_print(param_value) {
		if (isObject(param_value)) {
			for key, value in param_value {
				if key is not number
				{
					output .= """" . key . """:"
				} else {
					output .= key . ":"
				}
				if (isObject(value)) {
					output .= "[" . this._print(value) . "]"
				} else if value is not number
				{
					output .= """" . value . """"
				}
				else {
					output .= value
				}
				output .= ", "
			}
			return subStr(output, 2)
		}
		return param_value
	}
	_stdOut(output:="") {
		try {
			fileAppend, % output, *
		} catch error {
			return false
		}
		return true
	}
	/**
	* Writes the report to a file and optionally opens the file.
	* @param {string} param_filepath - The path of the file where the report will be written. If not provided, the default logResultPath will be used.
	* @throws {exception} If there is an error writing the report to the disk.
	*
	* @returns {string} The report that was written to the file.
	* 
	* @example expect.writeResultsToFile(".\myLogFile.tap")
	*/  
	writeResultsToFile(param_filepath:=".\result.tests.log") {
		if (A_IsCompiled) {
			return 0
		}
		; prepare
		if (subStr(param_filepath, 1, 2) == ".\") {
			param_filepath := A_WorkingDir subStr(param_filepath, 2)
		}
		; create
		try {
			fileDelete, % param_filepath
		} catch {
			; do nothing
		}
		msgReport := this._buildReport() "`n"
        ;;Q3 this.logObj doesn't exist so no output written to file. 
        ;;But this.log does exist and produces output for writing to file.
		;;for key, value in this.logObj {
		for key, value in this.log {
			msgReport .= value "`n"
		}
		fileAppend, % msgReport, % param_filepath
		if (errorlevel == 1) {
			throw exception("Failed to write report to disk")
		}
		return msgReport
	}
	/**
	* returns the full test results. This should be used with to integrate with Continuous Integration (CI) Systems
	* @param {}
	*
	* @returns {string} returns a string containing all the test results.
	*
	* @example expect.report()
	*/
	report() {
		if (A_IsCompiled) {
			return 0
		}
		; create
		if (!this.finalTrigger) {
			this._final()
		}
        ;;Q4 This method does nothing - according to the docs is supposed to produce a 
        ;;msgbox with a 1-line summary of all the test results.
        ;;So have added msgbox code and commented out the return options as this is no longer needed.
        ;;Note this 'block' is same as in fullReport() apart from var name
        ;;of text to output. So could potentially be made into an internal method.
        ; choose the msgbox icon
		if (this.failTotal > 0)
			l_options := 48
        else
			l_options := 64
		msgbox, % l_options, expect.ahk, % this._buildReport()

		;;Q4 return this._buildReport()
	}
	/**
	* Uses msgbox to display the results of all tests with details of any failures
	* @param {} 
	*
	* @returns {string} The generated full report message.
	*
	* @example expect.fullReport()
	*/
	fullReport() {
		if (A_IsCompiled) {
			return 0
		}
		; create
		if (!this.finalTrigger) {
			this._final()
		}
		msgReport := this._buildReport()
		if (this.failTotal > 0) {
            ;;Q2a Commented this out as have added it to the _buildReport.
			;;msgReport .= "================================="
			loop % this.log.Count() {
				msgReport .= "`n" this.log[A_Index]
			}
		}
		; choose the msgbox icon
		if (this.failTotal > 0) {
			l_options := 48
		} else {
			l_options := 64
		}
		msgbox, % l_options, expect.ahk, % msgReport
		return msgReport
	}
;; end of expect.ahk
}
Each code difference has ;;Qn added to the code to describe the change in more detail. Each of these are also described below:

;;Q1 Commenting out the next 3 lines means that the test label text is not replaced with the test note text.

;;Q2 Changes to the underlining of the 1-line summary of tests constructed in this._buildReport().
;;Q2a Commented the underlining out here as have added it to _buildReport.
;;Q2b I've added underlining here and commented out in the 1 other place. So the underlining is added to both the msgbox and output to file.

;;Q3 this.logObj doesn't exist so no output written to file. But this.log does exist and produces output for writing to file.

;;Q4 .report() does nothing - according to the docs is supposed to produce a msgbox with a 1-line summary of all the test results. So have added msgbox code and commented out the return options as this is no longer needed. (Note this 'block' is same as in .fullReport() apart from var name of text to output. So could potentially be made into an internal method).

;;Q5 Code suggests _report() produces the 1-line test summary report to stdOut. But I don't understand why the stdout output needs to have hard coded "1.." or "# ". So I've commented these out, but left the generation of the 1-line test summary in.

;;Q6 By adding this code if a test in this class is called without a .label(), still add “== ===” in the place where the label would have been. If no labels at all are defined then adding this creates an improved visual demarking each test in the output. If some, but not all, tests use labels, then adding this means all tests are demarked thus, rather than a mix of some with and some without.

;;Q7 Done a lot of 'fiddling' to improve the output. I've made msg apply to stdout and a new msg1 for reporting in msgboxs and to file. I've included a new var this.TestType - see Q8. Also added param_msg to the output when it exists.

;;Q8 Added this.TestType so output can identify what each test was (equal, undefined, etc).


Hopefully I haven't busted any functionality or rode rough-shot over what you'd been working towards as output.

Look forward to hearing what you think in due course.

User avatar
Chunjee
Posts: 1418
Joined: 18 Apr 2014, 19:05
Contact:

Re: [Class] expect.ahk (rapid unit testing)

Post by Chunjee » 10 Apr 2024, 11:36

I for sure see an issue with .writeResultsToFile and that's my fault as I don't use that feature much yet. Fix should be on github hopefully today along with some tests as there are currently NONE for that method :o

DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 11 Apr 2024, 03:51

Chunjee wrote:
10 Apr 2024, 11:36
I for sure see an issue with .writeResultsToFile and that's my fault as I don't use that feature much yet. Fix should be on github hopefully today along with some tests as there are currently NONE for that method :o
Perfect, really appreciate you taking a look at my observations :thumbup:

DaveT1
Posts: 224
Joined: 07 Oct 2014, 11:23

Re: [Class] expect.ahk (rapid unit testing)

Post by DaveT1 » 11 Apr 2024, 04:05

Hi Chunjee,

sorry to bombard you, but I've been pretty focussed on try to get this up and running for my use and in doing so I think I've spotted another issue:

There might be a problem with true() (and therefore maybe false()?)…. consider the example from the docs: expect.true(InStr("String", "S")). This returns “1” (a proxy for true), ie “S” is in “String”. So all good here.

But change the example to expect.true(InStr("String", "t")) and this returns “0” (a proxy for false), ie the result of this test tells us that “t” is not in “String”. Clearly not correct.

The issue is on this line: if (param_actual == param_expected), where, via the calling parameters:
param_actual is the result of InStr, and
param_expected is always set as true (ie “1”).

And the nub of the issue is that InStr returns the position of the needle in the haystack. So InStr("String", "S") returns “1” and InStr("String", "t") returns “2”. So for the last case the comparison becomes if (2 == 1) which obviously fails.

I gave some thought as to how true() could be changed to allow for all the cases it could potentially deal with and I couldn't see a way to do this. Potentially have 2 true() methods each written for specific cases? Or pass the test being done as a string and have expect perform the test?

Sorry to be the bearer of more problems - hope you know I'm only trying to help?

User avatar
Chunjee
Posts: 1418
Joined: 18 Apr 2014, 19:05
Contact:

Re: [Class] expect.ahk (rapid unit testing)

Post by Chunjee » 11 Apr 2024, 22:14

Yes inStr returns the location. v1 does not have a built-in 'contains' function or that would be better. I believe most examples have been switched to a perhaps clearer expect.true((1 == 1))

Additionally ahk cannot differentiate true from 1

Post Reply

Return to “Scripts and Functions (v1)”