Join 34,000+ subscribers and receive articles from our blog about software quality, testing, QA and security.
 

Copying a whole project


#1

Hi,

We currently use Templates projects for new future projects. we didn’t find anyway to copy a whole project so we ended up with a C# script that makes several API calls:

Dictionary<string, string> changes = new Dictionary<string, string>();
Dictionary<string, object> data;
Int32 i;
Int32 j;
Int32 k;
JObject oldProject = (JObject)Client.SendGet("get_project/" + currentProject["id"].ToString());
data = new Dictionary<string, object>
{
    {"name", Zt_NewName.Text},
    {"announcement", oldProject["announcement"]},
    {"show_announcement", oldProject["show_announcement"]},
    {"suite_mode", oldProject["suite_mode"]}
};
Et_Progress.Text = "Adding project " + data["name"].ToString(); Application.DoEvents();
JObject newProject = (JObject)Client2.SendPost("add_project", data);
JArray oldMilestones = (JArray)Client.SendGet("get_milestones/" + currentProject["id"].ToString());
i = 0;
foreach (var milestone in oldMilestones)
{
    data = new Dictionary<string, object>
    {
        {"name", milestone["name"]},
        {"description", milestone["description"]},
        {"due_on", milestone["due_on"]}
    };
    i += 1;
    Et_Progress.Text = "Adding milestone " + i + "/" + oldMilestones.Count() + " " + data["name"].ToString(); Application.DoEvents();
    JObject newMilestone = (JObject)Client2.SendPost("add_milestone/" + newProject["id"], data);
    changes.Add(milestone["id"].ToString(), newMilestone["id"].ToString());
}
JArray oldSuites = (JArray)Client.SendGet("get_suites/" + currentProject["id"].ToString());
i = 0;
foreach (var suite in oldSuites)
{
    data = new Dictionary<string, object>
    {
        {"name", suite["name"]},
        {"description", suite["description"]}
    };
    i += 1;
    Et_Progress.Text = "Adding suite " + i + "/" + oldSuites.Count() + " " + data["name"].ToString(); Application.DoEvents();
    JObject newSuite = (JObject)Client2.SendPost("add_suite/" + newProject["id"], data);
    changes.Add(suite["id"].ToString(), newSuite["id"].ToString());
    String parent_id = null;
    JArray oldSections = (JArray)Client.SendGet("get_sections/" + currentProject["id"].ToString() + "&suite_id=" + suite["id"]);
    j = 0;
    foreach (var section in oldSections)
    {
        data = new Dictionary<string, object>
        {
            {"name", section["name"]},
            {"description", section["description"]},
            {"suite_id", newSuite["id"]},
            {"parent_id", parent_id}
        };
        j += 1;
        Et_Progress.Text = "Adding section " + j + "/" + oldSections.Count() + " " + data["name"].ToString(); Application.DoEvents();
        JObject newSection = (JObject)Client2.SendPost("add_section/" + newProject["id"], data);
        JArray oldCases = (JArray)Client.SendGet("get_cases/" + currentProject["id"].ToString() + "&suite_id=" + suite["id"] + "&section_id=" + section["id"]);
        k = 0;
        if (oldCases.Count >= 1)
        {
            parent_id = null;
            foreach (var tcase in oldCases)
            {
                data = new Dictionary<string, object>
                {
                    {"title", tcase["title"]},
                    {"type_id", tcase["type_id"]},
                    {"priority_id", tcase["priority_id"]},
                    {"estimate", (tcase["estimate"].Count() != 0 ? tcase["estimate"] : 0) },
                    {"milestone_id", (String.IsNullOrEmpty(tcase["milestone_id"].ToString()) ? "" : changes[tcase["milestone_id"].ToString()])},
                    {"refs", tcase["refs"]},
                    {"custom_preconds", tcase["custom_preconds"]},
                    {"custom_steps_separated", tcase["custom_steps_separated"]},
                };
                k += 1;
                Et_Progress.Text = "Adding case " + k + "/" + oldCases.Count() + " " + data["title"].ToString(); Application.DoEvents();
                JObject newCase = (JObject)Client2.SendPost("add_case/" + newSection["id"], data);
                changes.Add(tcase["id"].ToString(), newCase["id"].ToString());
                Application.DoEvents();
            }
        }
        else
            parent_id = newSection["id"].ToString();
        Application.DoEvents();
    }
    Application.DoEvents();
}
JArray oldRuns = (JArray)Client.SendGet("get_runs/" + currentProject["id"].ToString());
i = 0;
foreach (var run in oldRuns)
{
    data = new Dictionary<string, object>
    {
        {"suite_id", changes[run["suite_id"].ToString()]},
        {"name", run["name"]},
        {"description", run["description"]},
        {"milestone_id", (String.IsNullOrEmpty(run["milestone_id"].ToString()) ? "" : changes[run["milestone_id"].ToString()])},
        {"assignedto_id", run["assignedto_id"]},
        {"include_all", run["include_all"]},
        {"case_ids", run["case_ids"]}
    };
    i += 1;
    Et_Progress.Text = "Adding run " + i + "/" + oldRuns.Count() + " " + data["name"].ToString(); Application.DoEvents();
    JObject newRun = (JObject)Client2.SendPost("add_run/" + newProject["id"], data);
    Application.DoEvents();
}
JArray oldPlans = (JArray)Client.SendGet("get_plans/" + currentProject["id"].ToString());
i = 0;
foreach (var plan in oldPlans)
{
    data = new Dictionary<string, object>
    {
        {"name", plan["name"]},
        {"description", plan["description"]},
        {"milestone_id", (String.IsNullOrEmpty(plan["milestone_id"].ToString()) ? "" : changes[plan["milestone_id"].ToString()])}
    };
    i += 1;
    Et_Progress.Text = "Adding plan " + i + "/" + oldPlans.Count() + " " + data["name"].ToString(); Application.DoEvents();
    JObject newPlan = (JObject)Client2.SendPost("add_plan/" + newProject["id"], data);
    Application.DoEvents();
    var runs = new Dictionary<string, object>();
    j = 0;
    JObject oldPlan = (JObject)Client.SendGet("get_plan/" + plan["id"].ToString());
    foreach (var entry in oldPlan["entries"])
    {
        foreach (var run in entry["runs"])
        {
            runs = new Dictionary<string, object>
            {
                {"suite_id", changes[run["suite_id"].ToString()]},
                {"name", run["name"]},
                {"description", run["description"]},
                {"milestone_id", run["milestone_id"]},
                {"assignedto_id", run["assignedto_id"]},
                {"include_all", run["include_all"]},
                {"config_ids", run["config_ids"]},
                {"case_ids", run["case_ids"]}
            };
        }
        data = new Dictionary<string, object>
        {
            {"suite_id", changes[entry["suite_id"].ToString()]},
            {"name", entry["name"]},
            {"assignedto_id", entry["assignedto_id"]},
            {"include_all", entry["include_all"]},
            {"runs", runs.ToArray()}
        };
        Et_Progress.Text = "Adding plan_entries " + i + "/" + oldPlans.Count() + " " + data["name"].ToString(); Application.DoEvents();
        JObject newPlan_entry = (JObject)Client2.SendPost("add_plan_entry/" + newPlan["id"], data);
    }
}
Et_Progress.Text = "Copy done!";
MessageBox.Show("END");

We would like to know if there is any another ways to copy an whole project without all these API calls (can easily be more than 1000 calls), faster and easier.

Thanks for your help/comments/suggestions, it is really appreciated :smile:

-Steve


#2

Hello Steve,

Thanks for your posting! There’s currently no direct feature to copy a project but you can use the API for this as you mentioned. Could you please let me know why you need or want to create project copies? I’m asking as there are usually better ways to implement certain use cases or scenarios with TestRail and I’m happy to provide suggestions if you can share a few more details.

For example, instead of creating a new project for a new version or release, you can use milestones in TestRail and keep using a single project. If you want to add a new project because of different case versions or repositories (e.g. for different versions or releases), you can also use the advanced versioning features of TestRail via TestRail’s so called baselines:

http://blog.gurock.com/postings/introducing-testrail-4-0/2757/

(see “Suite Modes and Baselines”)

I hope this helps and please let me know in case you have any further questions!

Regards,
Tobias


#3

Hi Tobias and thanks for your fast answer.

We are in the Video Games industry, we currently have multiple games (projects) in development. We have assigned one project per game title with various milestones in between, and test suites compiled with many test cases for the various departments. As not all of our departments are fixed on one project we plan to copy Project templates over for the teams that have repeated checks between projects.

I hope this gives you more information.


#4

Thanks, Steve, that helps a lot.

I would currently recommend using the API in this case as this would be most flexible solution. You can also choose to copy just individual test suites and not an entire project if needed. An alternative is to use the Copy & Move Test Cases dialog via the UI and the copy/move icon when viewing a test suite (the icon with two sheets of paper). We will also make sure to look into copying an entire project or parts of a project for a future version, thanks again for your feedback on this!

Regards,
Tobias