Toolboxcategory cloudIntegrations - Surround SCM Integrations - TestTrack QA Wizard Pro SDK Examples Scripts - QA Wizard Pro Surround SCM Surround SCM API Examples Surround SCM Plug-in Samples Surround SCM Reporting Dashboards TestTrack TestTrack Reporting Dashboards TestTrack SDK Tutorials Triggers - TestTrack Triggers and Scripts - Surround SCM |
ViewsPersonal toolsTestTrack SOAP SDK Tutorial - C SharpFrom Seapine Labs(Redirected from SDK C Sharp Tutorial)
This article includes everything you ever wanted to know about writing TestTrack SDK applications in C#! Don't like C#? We have tutorials for a variety of languages. Be sure to check out the TestTrack SDK Help pages for more information. Want Seapine to write your SOAP app for you? Email us for more information.
[edit] Getting StartedYou must install the TestTrack SDK as part of your server installation. If you haven't done this, you'll need to run the TestTrack installer for the version you have installed. Once installed, there are 2 files of interest.
Once you've installed the TestTrack SDK, you can pull it into Visual Studio. Within your Visual Studio project, right-click the project and choose Add Web Reference. In the URL field, enter the address to your TestTrack SDK installation, something like:
Click Go. Visual Studio should find one web reference at that address. Change the web reference name to TTSoap then click Add Reference. Finally, import the namespace and you're ready to roll.
An alternative method is to generate the stub code manually, from the wsdl. The downside is you'll have to do this each time you upgrade to a newer version of TestTrack. [edit] Create a ConnectionThe TestTrack SDK requires authentication before you can retrieve and save data. ttsoapcgi cgiengine = new ttsoapcgi(); // Set the URL, based on your SDK installation. cgiengine.Url = "http://myserver/cgi-bin/ttsoapcgi.exe"; // Fetch a list of projects you have access to. CProject[] aproject = cgiengine.getProjectList( "administrator", ""); // Or, build your own. CProject project = new CProject(); project.database = new CDatabase(); project.database.name = "MyProject"; project.options = new CProjectDataOption[3]; project.options[0] = new CProjectDataOption(); project.options[0].name = "TestTrack Pro"; // add TTP functionality. project.options[1] = new CProjectDataOption(); project.options[1].name = "TestTrack TCM"; // add TCM functionality. project.options[2] = new CProjectDataOption(); project.options[2].name = "TestTrack RM"; // add RM functionality. // Login. long lSession = cgiengine.ProjectLogon( project, "administrator", ""); ...do some stuff... // When you're finished, log off. cgiengine.DatabaseLogoff(lSession); Things to Know:
[edit] Query ObjectsThere are two ways to retrieve data through the TestTrack SDK. You can explicitly call a getObject method or you can call the getRecordListForTable method. [edit] getObjectCalling the get method on an object is useful when you know exactly which object you need. For performance reasons, this is not recommended when you want to extract data from multiple objects of the same type. // retrieve defect #45, with attachments. CDefect def = cgiengine.getDefect(lSession, 45, "", true); // retrieve test case #312, with attachments. CTestCase tCase = cgiengine.getTestCase(lSession, 312, "", true); // retrieve requirement #23, with attachments. CRequirement req = cgiengine.getRequirement(lSession, 23, "", true); // retrieve requirement document #3, with attachments. CRequirementDocument reqDoc = cgiengine.getRequirementDocument(lSession, 3, "", true); [edit] getRecordListForTableIf you'd rather query multiple objects of the same type, similar to a SELECT statement in SQL, use getRecordListForTable. This method allows you to specify what data you want to retrieve and apply a filter to the results.
// #, summary, custom field, product and type
string[] astrFields = new string[] {"Number", "Summary", "My Custom", "Product", "Type"};
CTableColumn[] atc = new CTableColumn[astrFields.Length];
for (int i = 0; i < astrFields.Length; ++i)
{
atcFetch[i] = new CTableColumn();
atcFetch[i].name = astrFields[i];
}
// fetch all defects
CRecordListSoap rows = cgiengine.getRecordListForTable( lSession, "Defect", "", atcFetch);
When calling getRecordListForTable you must specify both the object type you want to query and an array of fields you want to retrieve. You can also optionally specify a filter that you've pre-configured in TestTrack. All of this information can be hard-coded as shown in the previous example, or dynamically passed as shown below. // What object types can I query? CDatabaseTable[] adt = cgiengine.getTableList(lSession); // What field data is available for a given object type? CTableColumn[] atc = cgiengine.getColumnsForTable( lSession, adt[?]); // What filters are available? CFilter[] af = cgiengine.getFilterList(lSession); Things to Know:
[edit] Querying the Requirement DocumentA Requirement Document is a collection of requirements. If you need a list of the requirements associated with a specific document you can use the getRequirementIDsForDocument() call. //get a list of requirement numbers associated with Requirement Document #4 long[] reqs = cgiengine.getRequirementIDsForDocument(lSession, 4); //Returns requirement numbers, not record ids. [edit] Create ObjectAdding an object is simply a matter of creating a new instance and calling the addObject method. For example, to create a defect: // Create the CDefect object. CDefect def = new CDefect(); def.summary = "This is a new defect"; def.product = "My Product"; def.priority = "Immediate"; // Add the defect to TestTrack. long lNewNum = cgiengine.addDefect(lSession, def); Things to Know:
[edit] Update ObjectBefore updating an object, you must first lock it for editing by calling editObject. // Open defect #11 for editing. CDefect def = cgiengine.editDefect(lSession, 11, "", true); // Change the Priority. def.priority = "Immediate"; // Save the defect changes cgiengine.saveDefect(lSession, def); // Or, you can release the edit lock w/o saving changes. cgiengine.cancelSaveDefect(lSession, def2.recordid); Things to Know:
[edit] Update Custom Field
// Lock the defect for edit.
CDefect def = cgiengine.editDefect(lSession, 1284, "", false);
// Find and update the 'My Custom' custom field.
for (int i = 0; i < def.customFieldList.Length; ++i)
{
if (def.customFieldList[i].name == "My Custom")
((CStringField)def.customFieldList[i]).value = "Testing";
}
// Save changes.
cgiengine.saveDefect(lSession, def);
Things to Know:
[edit] Creating a defect with a custom fieldWhen creating a defect with a custom field, it appears that you have to fill in all of the custom fields. Otherwise you get a "Well-formedness error". To do this: CDefect def = new CDefect(); // ...fill in the other fields... CField[] customFields = cgiengine.getCustomFieldsDefinitionList(lSession, "Defect"); // Fill in the "Customer" field (a little LINQ here): CStringField customerField = (CStringField)customFields.First(f => f.name == "Customer"); customerField.value = "ACME"; def.customFieldList = customFields; long recordId = cgiengine.addDefect(lSession, def); [edit] Update Workflow StateTestTrack calculates state based on event history. This means you can't simply set a value to change state. Instead, you have to apply the necessary events to move the object into the desired state.
// Lock the defect for edit.
CDefect def = cgiengine.editDefect(lSession, 1284, "", false);
// Create the Fix event.
CEvent de = new CEvent();
de.name = "Fix";
de.resultingstate = "Fixed";
de.user = "System Administrator";
de.date = DateTime.Now;
de.fieldlist = new CField[1];
de.fieldlist[0] = new CDropdownField();
de.fieldlist[0].name = "Resolution";
((CDropdownField)de.fieldlist[0]).value = "Code Change";
// Add the event to defect's eventlist.
if (def.eventlist == null || def.eventlist.Length==0)
{ // No events, so we can just create a new list.
def.eventlist = new CEvent[1];
def.eventlist[0] = de;
}
else
{ // Append new event to end of existing list.
ArrayList list = new ArrayList();
for (int i = 0; i < def.eventlist.Length; ++i)
list.Add(def.eventlist[i]);
def.eventlist = new CEvent[def.eventlist.Length + 1];
for (int i = 0; i < list.Count; ++i)
def.eventlist[i] = (CEvent)list[i];
def.eventlist[def.eventlist.Length] = de;
}
// Save our changes.
cgiengine.saveDefect(lSession, def);
[edit] Linking ObjectsYou can link defects, test cases, test runs, requirements and requirement documents within TestTrack. [edit] Defect LinkCLink link = new CLink(); link.linkDefinitionName = "Related Items"; link.comment = "This is a new link!"; link.childList = new CLinkedItem[2]; link.childList[0] = new CLinkedItem(); link.childList[0].tablename = "Defect"; link.childList[0].entityID = 1; // CDefect.recordid (not DefNum) link.childList[1] = new CLinkedItem(); link.childList[1].tablename = "Defect"; link.childList[1].entityID = 2; // CDefect.recordid (not DefNum) long lResult = cgiengine.addLink(lSession, link); [edit] Test Case LinkCLink link = new CLink(); link.linkDefinitionName = "Related Items"; link.comment = "This is a new link!"; link.childList = new CLinkedItem[2]; link.childList[0] = new CLinkedItem(); link.childList[0].tablename = "Defect"; link.childList[0].entityID = 5; // CDefect.recordid (not DefNum) link.childList[1] = new CLinkedItem(); link.childList[1].tablename = "Test Case"; link.childList[1].entityID = 2; // CTestCase.recordid (not TCNum) long lResult = cgiengine.addLink(lSession, link); [edit] Test Run LinkCLink link = new CLink(); link.linkDefinitionName = "Parent/Child"; link.comment = "This is a new link!"; link.linkparent = new CLinkedItem(); link.linkparent.tablename = "Defect"; link.linkparent.entityID = 5; // CDefect.recordid (not DefNum) link.childList = new CLinkedItem[1]; link.childList[0] = new CLinkedItem(); link.childList[0].tablename = "Test Run"; link.childList[0].entityID = 2; // CTestRun.recordid (not TRNum) long lResult = cgiengine.addLink(lSession, link); [edit] Requirement LinkCLink link = new CLink(); link.linkDefinitionName = "Parent/Child"; link.comment = "This is a new link!"; link.linkparent = new CLinkedItem(); link.linkparent.tablename = "Defect"; link.linkparent.entityID = 5; // CDefect.recordid (not DefNum) link.childList = new CLinkedItem[1]; link.childList[0] = new CLinkedItem(); link.childList[0].tablename = "Requirement"; link.childList[0].entityID = 2; // CRequirement.recordid (not ReqNum) long lResult = cgiengine.addLink(lSession, link); [edit] Requirement Document LinkCLink link = new CLink(); link.linkDefinitionName = "Parent/Child"; link.comment = "This is a new link!"; link.linkparent = new CLinkedItem(); link.linkparent.tablename = "Defect"; link.linkparent.entityID = 5; // CDefect.recordid (not DefNum) link.childList = new CLinkedItem[1]; link.childList[0] = new CLinkedItem(); link.childList[0].tablename = "Requirement Document"; link.childList[0].entityID = 2; // CRequirementDocument.recordid (not ReqDocNum) long lResult = cgiengine.addLink(lSession, link); [edit] Add File Attachment
// Lock the defect for edit.
CDefect def = cgiengine.editDefect(lSession, 1284, "", false);
// Create the file attachment.
string strFile = "C:\\readme.txt";
CFileAttachment file = new CFileAttachment();
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
StreamReader reader = new StreamReader(strFile);
file.mstrFileName = Path.GetFileName(strFile);
file.mpFileData = enc.GetBytes(reader.ReadToEnd());
reader.Close();
// Add the attachment to the defect.
CReportedByRecord reprec = def.reportedbylist[0];
CFileAttachment[] afile = reprec.attachmentlist;
if (afile == null)
{
def.reportedbylist[0].attachmentlist = new CFileAttachment[1];
def.reportedbylist[0].attachmentlist[0] = file;
}
// should add else here, to handle defects with existing attachments.
// Save our changes.
cgiengine.saveDefect(lSession, def);
[edit] Promote User/CustomerAt times you may need to turn a local user or customer into a global account. There are essentially two ways to do this. You can promote them as a new global user, or you can link them to an existing global user.
// Promte as new global user int iResult = cgiengine.promoteUser(lSession, "John Bark", null, "jbark"); // check that iResult == 0
// Promte to existing global user int iResult = cgiengine.promoteUser(lSession, "John Bark", "Sarah Kaiser", null); // check that iResult == 0 Note: The name parameters are searched based on your user settings in TT. So if you have names setup to display as 'lname, fname', then that's the format you should use when passing them to the call. If you passed them exactly as shown in the examples above (fname lname), they won't be found and the promote will fail. [edit] Adding Drop-down ValuesThe basic process is very easy, you just create a value object and add it to the field. CFieldValue[] aval = new CFieldValue[1]; aval[0] = new CFieldValue(); aval[0].value = "TEST1"; cgiengine.addDropdownFieldValuesForTable( lSession, "Test Run", "Type", aval); For a complete listing of the tables and fields available, you can run this:
CDatabaseTable[] atable = cgiengine.getTableList(lSession);
for (int i = 0; i < atable.Length; ++i)
{
try {
CTableField[] afld = cgiengine.getDropdownFieldForTable( lSession, atable[i].name);
} catch (Exception p_exception) { /* throws exception, if no fields exist on table */ }
}
[edit] TroubleshootingWith C# and Visual Studio, using the TestTrack SDK is pretty simple but it never hurts to have some tools to help with debugging.
|
|


