Help:Pickle/Expect

From mediawiki.org

There is a test-lib based on expectations, that is a we say what the subject is expected to do, not what is a failed response. This is slightly different from the assert style testing libs. Expectations are common in several testing frameworks, but the style implemented has its own quirks to make it work in our environment.

The main difference is that while an assertion says whats wrong and stops execution, while expectation says whats right and continue execution. In addition there are some magic behind the scenes to allow the subjects and expectations to be set up for repeated testing.

Note that part of the naming scheme is slightly odd and should perhaps be changed. The call expect() should not set the subject but the expectation. Not quite sure whats easier to understand.

Core idea[edit]

There are three access functions; subject, expect, and reports. They all act as accessors to several internal structures, but in particular the first two act as accessors to a condition object. The first two also has their own stacks for storing objects assigned to them. That makes it possible to keep subjects and expectations for later testing, and especially to set up partial fixtures for repeated testing.

There are several ways to implement objects in Lua, but we can safely assume that most objects we want to test will be based on tables. By implementing additional accessors we can test other types of objects too, but most of the time we we will be testing tables.

Assume we have a rather simple library foo on the form

local h = {
  bar = function() return 'baz' end
}

return h

This i pretty typical for most libraries, and we can include it in other libraries as

local foo = require 'foo'

and this can set in our test fixtures as

subject = require 'foo'

The form local subject = require 'foo' should not be used, as this will not work. This creates a local variable, while we want to assign the required lib to an existing variable. Or really assign to the table.

It is also possible to set the testee as a temporal, by instead use a call syntax, like subject( require 'foo' ). This will place the object in a temporary store in the condition object created by the access method.

There is one important thing; note one important thing; we are testing the object returned from the library, and if that maintain a state it will be retained from call to call. We may say that we are testing a specific instance of the object and not the library. This can lead to bogus tests, so be careful!

In its simple form it is already ready for testing. The instance from the submerges in the subject = require 'foo' line and emerges in the conditions structure. Because that is available through subject and expect we can add simple tests like

subject = require 'foo'
subject :bar() :toBe( 'baz' )

or in call-style

subject(require 'foo') :toBe( 'baz' )

The final toBe( 'baz' ) does a little magic, it sets the opposite value of the initial access method. If the initial access method is subject then it sets the temporal expect, if it is expect then it sets the temporal subject. It will not push the value on the subject or expect stacks.

Note in particular that these gives the same result (here the testee is not a table and is defined inline)

subject = 'a'
expect :toBe( 'a' )
expect( 'a' ) :toBeSame()

and that also this is the same

subject = 'a'
expect = 'a'
subject :toBeSame()
expect :toBeSame()
condition :toBeSame()

The results from the tests are accessible through reports. In a Mediawiki-context the reports can be shown by calling mw.log

expect( 'baz' ) :toBe( 'baz' )
mw.log( report() )

Library[edit]

Preprocesses[edit]

Preprocesses or conditioners are those methods that are run before the similarity test, which is our idea of what is a correct answer. After the similarity test there are additional postprocesses to transform the test into our final outcome. That makes it possible to do a positive identification of a state and then negate that state, which is often easier to do than to test for the negative outcome.

Preprocesses can be attached to both subjects and expectations, which gives slightly different testing styles. If the chain is started by subject then the preprocesses attach to the subject, while if it starts with expect then the preprocesses attach to the expectation. It is possible to start attaching to one of them and then shift to the other one.

Picks[edit]

Functions may return multiple values, and there are a number of methods to extract a specific value. Typical use is like

expect('a', 'b', 'c') :second() :toBe('a')

The methods are named

  • first
  • second
  • third
  • fourth
  • fifth
  • sixth
  • seventh
  • eight
  • ninth
  • tenth
  • eleventh
  • twelfth

Casting[edit]

A few methods for casting

subject( '42' ) :asNumber() :toBe( 42 )
subject( 42 ) :asString() :toBe( '42' )
  • asNumber
    • number
  • asString
    • string

Formatters[edit]

There are formatters to shift from one format to another

Formatters for strings (second levels are aliases)

  • asUpper
    • upper
    • asUC
    • uc
  • asLower
    • lower
    • asLC
    • lc
  • asUpperFirst
    • upperfirst
    • asUCFirst
    • asUCfirst
    • ucfirst
  • asLowerFirst
    • lowerfirst
    • asLCFirst
    • asLCfirst
    • lcfirst

Formatters for ustrings (second levels are aliases)

  • … same as methods for strings, except they has an "u" attached
  • asNFC
    • nfc
  • asNFD
    • nfd

Formatters for numbers

  • asInteger
    • integer
  • asFraction
    • fraction
  • asRound
    • round
  • asFloor
    • floor
  • as Ceil
    • ceil