BackgroundQBO 3 uses the Jasmine Behavior Driven Development (BDD) framework for unit testing functionality. As project managers develop features, they shall draft a test spec following BDD guidelines. For example, the notification behavior feature includes the following test spec:
Existing test specs can be viewed from the standard QBO 3 main menu's Design > Specifications. To implement a test spec, the following steps should be taken:
See existing specs (such as qbo.ContactWeb > Scripts > Specs > Contact > Spec.Contact.js) for examples. Useful SpecsThe following specs can quickly tell you if a QBO system is installed and configured properly:
Sample Specification The Security > Login spec performs the following tests:
In order to perform these tests, the script will invoke the Login, Person, and SecurityAccess modules. The javascript code to wire that is: var login = qbo3.getJasmineObject('LoginObject', 'results'); var person = qbo3.getJasmineObject('PersonObject', 'results'); var access = qbo3.getJasmineObject('SecurityAccessObject', 'results'); The qbo3.getJasmineObject is a helper method (found in /Templates/Specs/qbo.SpecHelpers.js). It instantiates a qbo-based javascript class (e.g. new qbo3.PersonObject()), and automatically wires the class to be used in a spec. This includes tying into event handlers for JSON responses and failures. To test creation of a person: it('New users can be created', function () { person.save({ 'Person': 'Spec Test User - ' + person.spec.uniqueID, 'Email': testEmail, 'FirstName': person.spec.uniqueID, 'LastName': 'SpecUser', 'Active': 1 }); expect(person.properties.PersonID).toBeDefined(); }); In this case, we simply invoke the Person.ashx/Save to create a new user. Note that the Person.Person includes a unique ID, so we can later search for that unique name if we need to. If we get a PersonID in the response, we consider the test a success. To test the password reset request functionality: it('Can request a password reset', function () { login.resetPassword({ 'Person': 'Spec Test User - ' + person.spec.uniqueID }); expect(login).toHaveElement('!body div.alert-success'); }); In this case, success is defined as receiving an alert-success message in the UI. The next test checks to see if a one-time access (SecurityAccess) row was properly created for the new user: it('allows resetting of a password via a secure link', function () { access.invokeJson('Search', { 'PersonID': person.properties.PersonID }); expect(access.spec.json.SecurityAccessCollection.SecurityAccessItem[0].SecurityAccessID).toBeDefined(); login.invoke('Impersonate', { 'PersonID': person.properties.PersonID }); expect(login.spec.errors).toEqual(0); }); Note the use of Impersonate here; this is incredibly useful for testing various security scenarios. Impersonation is logged, and can be disabled site-wide for PROD sites. However, it's very useful for testing. The next 4 tests simply call Login.ashx/SetPassword, 3 with invalid passwords, and the 4th with a valid password: it('does not allow short passwords', function () { login.invokeJson('SetPassword', { 'Password': 'Easy' }); expect(login.spec.json.success).toBeFalsy(); }); it('does not allow weak passwords', function () { login.invokeJson('SetPassword', { 'Password': 'EasyPeasy' }); expect(login.spec.json.success).toBeFalsy(); }); it('does not allow key words', function () { login.invokeJson('SetPassword', { 'Password': 'QuandisRocks!' }); expect(login.spec.json.success).toBeFalsy(); }); it('does allow strong passwords', function () { login.invokeJson('SetPassword', { 'Password': login.spec.uniqueID + 'A!' }); expect(login.spec.json.success).toBeTruthy(); }); Lastly, we clean up the data recently inserted: it('Can delete users', function () { login.invoke('UnImpersonate'); expect(login.spec.errors).toEqual(0); person.invoke('Delete', { 'PersonID': person.properties.PersonID }); }); Helper MethodsWhen calling qbo3.getJasmineObject, the object returns has a 'spec' object attached: instance.spec = { uniqueID: String.uniqueID(), // used to track data so you know where it came from idList: new Array(), // keep a list if IDs inserted, so you can delete them as part of cleanup errors: 0, // each time an error is caught, this is incremented data: null, // stores XML documents received when calling invokeXml json: null // stores JSON objects received when calling invokeJson } The spec.json is probably the most useful feature; it allows you to easily process any JSON responses from the server very quickly. Additional helper methods in the qbo3.jasmine object include:
LoggingSpec test results log to the Message table:
--pattern:
--SELECT * FROM Message WHERE MessageType = 'SPECLOG' --AND OBJECT = [SUITE NAME] --AND MESSAGE = [IT STATEMENT]
-- examine STATUS column for PASS/FAIL results--examples of pulling data related to Decision/Spec.Decision.js:
--get all messages relevant to suite 'Workflow (Decision Processing)', most recent first SELECT * FROM MESSAGE WHERE MESSAGETYPE = 'SPECLOG' AND OBJECT = 'WORKFLOW (DECISION PROCESSING)'order by MessageID desc
--get all messages relevant to SUITE = 'Workflow (Decision Processing)' and It Statement = 'can create an ImportForm step', most recent first SELECT * FROM MESSAGE WHERE MESSAGETYPE = 'SPECLOG' AND OBJECT = 'WORKFLOW (DECISION PROCESSING)'AND Message = 'CAN CREATE AN IMPORTFORM STEP'order by MessageID desc Road MapFuture versions of QBO3 will support the following spec enhancements:
|