如何使用Coded UI Test对Webpage进行自动化测试

 🔥 交流讨论:欢迎加入我们一起学习!

🔥 资源分享耗时200+小时精选的「软件测试」资料包

🔥 教程推荐:火遍全网的《软件测试》教程  

📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

在Visual Studio中,Coded UI Test已经不是什么新特性了,较早版本的Visual Studio中就已经有这个东东了。它主要用来帮助自动化测试工程师和开发人员确保程序在UI方面没有任何问题。这其中包含了丰富的内容。在这之前,我一直对自动化测试的工作以及什么是自动化测试一知半解,具备自动化测试编码能力的工程师所掌握的技能在某种程度上要远超程序开发人员和设计人员,对于这一点,我早有耳闻!但直到亲身体验我才确信,测试工作远没有我们想象得那么简单。开发人员或许花上数小时就可以完成项目中某一个独立模块并使其在一定范围内正常运行,然而,自动化测试工程师也许会花上好几天的时间来编写对应的自动化测试代码来确保这一功能运行正常。

  Coded UI Test包含了十分丰富的API库,它可以录制和回放UI操作,捕捉UI元素并获取属性的值,并生成操作代码。测试人员在生成代码的基础上对测试对象的值进行逻辑判断并给出测试结果。创建一个Coded UI Test很容易,大多数情况下,我们只需要借助于Visual Studio就可以完成绝大部分操作。为了说明整个操作过程,我们假设一个测试需求:

在浏览器中打开百度搜索,输入“jaxu cnblogs”关键字,搜索并查看结果的第一条是否为“Jaxu - 博客园”

基本操作

  (本文演示的所有代码和操作均在Visual Sutdio 2013和Windows 8.1 + IE 11环境下)

  在Visual Studio中开始创建一个Coded UI Test Project。这很简单!

  工程创建成功后,Visual Studio会问你是马上开始一个新的UI录制还是选择已经录制好的操作。当然你也可以选择取消,在后面的步骤里再开始UI录制。

  工程默认生成CodedUITest1.cs文件。在开始录制UI操作之前,对基本概念做一下介绍:

  1. Coded UI Test工程的运行是从包含有[CodedUITest]特征属性的类开始的。一个工程中可以有多个这样的类。
  2. 与普通的工程不同,我们不能通过F5或者点击Visual Studio中的运行按钮来调试或直接运行工程,Coded UI Test工程必须通过Test Explorer或者在包含有[CodedUITest]特征属性的类中来选择运行相应的测试方法。
  3. 在带有[CodedUITest]特征属性的类中,所有的测试方法都必须带有[TestMethod]特征属性,以表示它是一个有效的测试方法,可以直接运行。
  4. 通过选择TEST->Windows->Test Explorer可以打开Test Explorer窗口,在Test Explorer窗口中可以查看工程中所有的测试方法并选择运行。当然,你也可以在带有[TestMethod]特征属性的测试方法的代码块中右键选择运行该测试方法。
  5. 测试方法同样可以调试。在选择运行测试方法时,你会看到有Debug Test的菜单,调试的过程和在普通工程中一样。

  然后,我们开始一个UI录制。在工程中添加一个Coded UI Test Map文件。创建成功后Visual Studio会自动在屏幕的右下角打开Coded UI Test Builder窗口,以方便我们进行UI录制操作。

  借用MSDN上的图片来对Coded UI Test Builder窗口上按钮的功能做一下简单的说明:

  • Record icon - 开始一个UI操作的录制。例如菜单导航、按钮点击等操作。
  • Edit steps - 对录制的步骤进行编辑,调整或者删除冗余的步骤。
  • Add assertions - 不仅仅是添加断言,通过点击该按钮并拖放到测试对象的UI上以选取控件,然后你可以添加断言。
  • Generate code - 这一步很关键,在完成以上所有的操作后,通过点击该按钮Visual Studio会自动为你生成代码。生成的代码在.uitest文件下面的.Desinger.cs文件中可以看到。注意不要手动修改自动生成的代码,这会导致下次通过Coded UI Test Builder窗口对.uitest文件进行修改时某些对象或操作不同步。
  • Close to finish recoding - 在关闭Coded UI Test Builder窗口之前,确保所做的修改已经生成了对应的代码。如果要修改.Designer.cs文件中自动生成的代码,可以在Solution Explorer中右键选择.uitest文件,然后选择Edit With Coded UI Test Builder。在后面的步骤中我们会讲到这一点。

  UI Action的录制和UI控件的选择操作是分开的。让我们先开始UI Action的录制。

  1. 点击Coded UI Test Builder窗口中的红色按钮
  2. 打开IE浏览器,在地址栏中输入http://www.baidu.com以导航到百度搜索引擎
  3. 输入关键字“jaxu cnblogs”,点击“百度一下”
  4. 在Coded UI Test Builder窗口中点击Pause,然后点击Edit steps,删除不必要的步骤。我删除了操作中的第4步和第5步。
  5. 选择Generate code,对所要生成的方法取个名字,然后点击Add and Generate按钮。Visual Studio为我们所录制的UI操作步骤生成了对应的代码。
  6. 关闭Coded UI Test Builder窗口,在Visual Studio中查看刚才生成的代码。

  在Solution Explorer中展开UIMap1.uitest文件,选择并打开UIMap1.Designer.cs文件,可以看到刚才所生成的代码。是不是很想现在就运行一下,来看看这些自动生成的代码如何运行?现在还不行,因为单纯的UI Action运行没有任何意义,Coded UI Test的真正意义是通过UI操作来定位到UI上的某一个特定元素,并最终通过断言来确定该元素的属性是否和预期的值相等。

  为了能够手动修改.Designder.cs文件中生成的代码,我们需要将它们移到.cs文件中。在Solution Explorer中双击UIMap1.uitest文件,在打开的窗口中我们可以看到左边是UI Actions所生成的步骤,右边是UI Control Map(稍后我们会用到它)。在左边的UI Actions中选择根节点RecordedMethod1,然后在顶部的菜单中选择Move code to UIMap1.cs,代码会被移到.cs文件以方便我们进行修改。完成该步骤之后,我们可以在.cs文件中看到这些代码并做相应的修改。

  你可能已经注意到了,自动生成的代码中有些对象的名字看起来并不那么好,甚至有些还包含了中文。你希望修改它们,但是不要在.Designer.cs文件中做任何修改!还记得前面我们讲过的Edit With Coded UI Test操作吗?在Solution Explorer中右键选择UIMap1.uitest文件,右键选择Edit With Coded UI Test打开Coded UI Test Builder窗口,然后点击Add assertions按钮(就是那个用来选择UI Control的按钮),然后展开UI Control Map界面。如下图,我们可以对其中生成的UI Controls进行编辑和重命名。

  完成修改之后再次点击Generate code按钮并关闭Coded UI Test Builder窗口,此时.Designer.cs文件中自动生成的代码已经做了修改。由于前面我们已经将相关的UI Actions部分的代码移到.cs文件里了,所以重命名的对象我们还需要在.cs文件中手动进行修改,否则编译时会出错。建议在将代码移到.cs文件之前完成自动生成代码的修改工作,以避免手动修改过多的代码。

  然后我们需要捕捉到百度搜索结果的UI控件,并对其中的结果进行判断。仍然使用Coded UI Test Builder窗口。

  1. 打开浏览器并导航到百度,输入关键字“jaxu cnblogs”并点击“百度一下”
  2. 在Coded UI Test Builder窗口中点击Add assertions按钮并拖放到浏览器窗口,同时指向你想要捕捉的控件上。有些控件很难精确捕捉到,没有关系,你可以先选中临近的控件,然后使用Coded UI Test Builder窗口中的方向按钮移动到你所要定位的元素。如下图,我们选择并定位到了百度搜索结果的DIV元素,并将该控件命名为UIContent_leftPane。
  3. 在Coded UI Test Builder窗口的左上角点击添加选择的控件,这一点很重要!忘记这一步则所选择的控件不会被添加到生成的代码中。
  4. 生成代码并关闭Coded UI Test Builder窗口。

  至此,所有的UI Actions和UI Controls都已经定义完毕,接下来我们要编码以完成对搜索结果的判断。借助于自动生成的代码,我们编写了下面的测试方法以实现文章最开始的测试需求。

复制代码

namespace CodedUITestProject2.UIMap1Classes
{
    using Microsoft.VisualStudio.TestTools.UITesting.HtmlControls;
    using Microsoft.VisualStudio.TestTools.UITesting.WinControls;
    using System;
    using System.Collections.Generic;
    using System.CodeDom.Compiler;
    using Microsoft.VisualStudio.TestTools.UITest.Extension;
    using Microsoft.VisualStudio.TestTools.UITesting;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard;
    using Mouse = Microsoft.VisualStudio.TestTools.UITesting.Mouse;
    using MouseButtons = System.Windows.Forms.MouseButtons;
    using System.Drawing;
    using System.Windows.Input;
    using System.Text.RegularExpressions;


    public partial class UIMap1
    {
        public void TestSearchResult()
        {
            HtmlDiv resultPanel = this.UINewtabInternetExplorWindow.UIJaxucnblogs_SearchDocument.UIContent_leftPane;
            HtmlDiv resultPanelFirst = (HtmlDiv)resultPanel.GetChildren()[0];
            HtmlHyperlink link = new HtmlHyperlink(resultPanelFirst);
            Assert.AreEqual("Jaxu - 博客园", link.InnerText, "Validation is failed.");
        }

        /// <summary>
        /// RecordedMethod1 - Use 'RecordedMethod1Params' to pass parameters into this method.
        /// </summary>
        public void RecordedMethod1()
        {
            #region Variable Declarations
            WinEdit uIItemEdit = this.UINewtabInternetExplorWindow.UIItemWindow.UIItemEdit;
            HtmlEdit uIWDEdit = this.UINewtabInternetExplorWindow.UIDocument.UIWDEdit;
            HtmlInputButton uISearchButton = this.UINewtabInternetExplorWindow.UIDocument.UISearchButton;
            #endregion

            // Go to web page 'about:Tabs' using new browser instance
            this.UINewtabInternetExplorWindow.LaunchUrl(new Uri("http://www.baidu.com"));

            // Type 'www.baidu{Enter}' in text box
            //Keyboard.SendKeys(uIItemEdit, this.RecordedMethod1Params.UIItemEditSendKeys, ModifierKeys.None);

            // Type 'jaxu cnblogs' in 'wd' text box
            uIWDEdit.Text = this.RecordedMethod1Params.UIWDEditText;

            // Click '百度一下' button
            Mouse.Click(uISearchButton, new Point(61, 18));
        }

        public virtual RecordedMethod1Params RecordedMethod1Params
        {
            get
            {
                if ((this.mRecordedMethod1Params == null))
                {
                    this.mRecordedMethod1Params = new RecordedMethod1Params();
                }
                return this.mRecordedMethod1Params;
            }
        }

        private RecordedMethod1Params mRecordedMethod1Params;
    }
    /// <summary>
    /// Parameters to be passed into 'RecordedMethod1'
    /// </summary>
    [GeneratedCode("Coded UITest Builder", "12.0.21005.1")]
    public class RecordedMethod1Params
    {

        #region Fields
        /// <summary>
        /// Go to web page 'about:Tabs' using new browser instance
        /// </summary>
        public string UINewtabInternetExplorWindowUrl = "about:Tabs";

        /// <summary>
        /// Type 'www.baidu{Enter}' in text box
        /// </summary>
        public string UIItemEditSendKeys = "www.baidu{Enter}";

        /// <summary>
        /// Type 'jaxu cnblogs' in 'wd' text box
        /// </summary>
        public string UIWDEditText = "jaxu cnblogs";
        #endregion
    }
}

复制代码

  大部分代码是由Coded UI Test Builder自动生成的,我们只编写了TestSearchResult()方法,用来寻找控件并获取到其中的值来进行判断。测试结果的判断通过Assert断言来完成,Assert提供了多种方法以帮助我们实现不同的判断,具体的内容可以参考msdn。然后对RecordedMethod1()方法做了适当修改。TestSearchResult()方法中对于如何查找和遍历UI控件在稍后的章节中会讨论到。然后我们将所有代码的调用放到CodedUITest1.cs文件中执行。

复制代码

[TestMethod]
public void CodedUITestMethod1()
{
    UIMap1 uimap = new UIMap1();
    uimap.RecordedMethod1();

    uimap.TestSearchResult();
}

复制代码

  现在可以通过Test Explorer窗口或者直接使用测试方法的上下文菜单运行或调试该测试方法。如果通过测试,测试方法前面会显示绿色的图标,否则会显示红色的叉。Visual Studio会为每次测试生成对应的测试报告,在工程目录下的TestResults文件夹中可以找到所有的测试报告。

有关Assert断言

  在自动化测试中,Assert断言一旦遇到测试失败的情况就会抛出异常,从而导致接下来的测试方法或任务不会继续执行。也就是说,如果一个测试工程中包含了诸多测试方法,经常的情况是一个测试工程中会包含很多个测试类,每个类针对不同的测试用例,而每个测试类中又包含了很多个不同的测试方法。面对如此庞大的一个测试工程,通常会花上数十分钟甚至数小时才能将预定好的所有测试方法跑完,我们当然不希望看到由于某一个测试方法失败而导致剩下的所有测试方法均不能得到执行。在自动化测试中,测试方法测试失败的情况是很普遍的,成功或失败都是一种结果,这总比程序运行到一半抛出异常要好得多。

  然而,Assert断言总会在测试失败的时候抛出异常,从而终止程序运行。如下面的测试方法,如果前两个断言中有任何一个失败的话,则剩下的断言不会被执行。

复制代码

[TestMethod]
public void CheckVariousSumResults()
{
    Assert.AreEqual(3, this.Sum(1001, 1, 2));
    Assert.AreEqual(3, this.Sum(1, 1001, 2));
    Assert.AreEqual(3, this.Sum(1, 2, 1001));
}

复制代码

  一个有效的解决办法是将每一个断言分别放到不同的测试方法中,如下面的代码:

复制代码

[TestMethod]
public void Sum_1001AsFirstParam_Returns3()
{
    Assert.AreEqual(3, this.Sum(1001, 1, 2));
}
[TestMethod]
public void Sum_1001AsMiddleParam_Returns3()
{
    Assert.AreEqual(3, this.Sum(1, 1001, 2));
}
[TestMethod]
public void Sum_1001AsThirdParam_Returns3()
{
    Assert.AreEqual(3, this.Sum(1, 2, 1001));
}

复制代码

  然而在大多数情况下这可能行不通。例如你需要测试一个包含100行的table,对每一行的title列进行text测试,在这种情况下你根本无法为每一个断言编写不同的测试方法。首先你无法确定测试方法的数量,其次过多的测试方法会增加维护成本。

  另一种我听到过的解决方法是使用参数化测试,然而据我所知,Coded UI Test中好像并不支持。在其它测试环境中或许有更好的解决办法。

  或许可以使用try-catch语句来截获Assert断言所抛出的异常,使程序能够继续运行下去。然后我们将所有截获到的异常信息输出到自定义的文件中,即自定义测试报告!测试报告可以是任意类型的文档,记事本或HTML比较常用。既然可以使用try-catch来截获Assert断言的异常欣喜,那么我们会很自然地想到使用下面的方法:

复制代码

[TestMethod]
public void CheckVariousSumResults()
{
    MultiAssert.Aggregate(
        () => Assert.AreEqual(3, this.Sum(1001, 1, 2)), 
        () => Assert.AreEqual(3, this.Sum(1, 1001, 2)), 
        () => Assert.AreEqual(3, this.Sum(1, 2, 1001)));
}

public static class MultiAssert
{
    public static void Aggregate(params Action[] actions)
    {
        var exceptions = new List<AssertFailedException>();

        foreach (var action in actions)
        {
            try
            {
                action();
            }
            catch (AssertFailedException ex)
            {
                exceptions.Add(ex);
            }
        }

        var assertionTexts = 
            exceptions.Select(assertFailedException => assertFailedException.Message);
        if (0 != assertionTexts.Count())
        {
            throw new
                AssertFailedException(
                assertionTexts.Aggregate(
                    (aggregatedMessage, next) => aggregatedMessage + Environment.NewLine + next));
        }
    }
}

复制代码

  上面的代码可以很有效地解决问题,但仍会存在问题。MultiAssert.Aggreate()方法中过多的断言最终会将所有的异常信息抛出,这会大大降低异常信息的可读性,不太利于我们从测试测试报告中分析出错的原因。要知道,测试方法最终的目的不是要让测试程序运行通过,而是通过测试报告来分析被测试对象可能具有的问题。

  下面是一个例子,可以用来有效地解决上面提出的问题。

复制代码

public static class AssertWrapper
{
    public static string AreEqual<T>(T expected, T actual, string message)
    {
        string result = null;
        try
        {
            Assert.AreEqual(expected, actual, message);
            TestLog.WritePass(message);
        }
        catch (AssertFailedException ex)
        {
            result = ex.Message;
            TestLog.WriteError(message);
        }
        catch (Exception ex)
        {
            result = ex.Message;
            TestLog.WriteError(message);
        }
        return result;
    }

    public static string AreEqual(string expected, string actual, string message)
    {
        string result = null;
        try
        {
            Assert.AreEqual(expected, actual, message);
            TestLog.WritePass(message);
            
        }
        catch (AssertFailedException ex)
        {
            result = ex.Message;
            TestLog.WriteError(result);
        }
        catch (Exception ex)
        {
            result = ex.Message;
            TestLog.WriteError(result);
        }
        return result;
    }

    public static string AreEqual(string expected, string actual, bool ignorecase, string message)
    {
        string result = null;
        try
        {
            Assert.AreEqual(expected, actual, ignorecase, message);
            TestLog.WritePass(message);

        }
        catch (AssertFailedException ex)
        {
            result = ex.Message;
            TestLog.WriteError(result);
        }
        catch (Exception ex)
        {
            result = ex.Message;
            TestLog.WriteError(result);
        }
        return result;
    }

    public static string Fail(string message)
    {
        string result = null;
        try
        {
            Assert.Fail(message);
        }
        catch (AssertFailedException ex)
        {
            result = ex.Message;
            TestLog.WriteError(result);
        }
        return result;
    }
}

复制代码

  AssertWrapper类中的方法可以有多个重载,以满足不同的需要,其基本思想就是使用try-catch语句来截获Assert断言所抛出的异常。TestLog类中的方法负责写测试报告,你可以将测试报告定义成任何形式。然后定义一个TestSettings类用来收集测试工程中所有的测试断言。

复制代码

public class TestSettings
{
    public static void AddResult(List<string> resultList, string result)
    {
        if (result != null)
        {
            if (resultList == null)
            {
                resultList = new List<string>();
            }
            resultList.Add(result);
        }
    }
}

复制代码

  在每一个.uitest文件的类中,这样使用上面的方法:

复制代码

public List<string> faillist;

public void ValidateHeader()
{
    TestSettings.AddResult(faillist,AssertWrapper.AreEqual(true, uIHeader.Exists, "test page: Validate Page header text"));
}

复制代码

  然后,在所有的测试方法中添加下面的代码(faillist为泛型List对象,被定义为TestMethod所在的类的私有变量,同时我们通过faillist.AddRange(testPage.faillist)语句将测试页面类中的泛型List内容添加过来):

复制代码

if (faillist != null && faillist.Count > 0)
{
    StringBuilder fail = new StringBuilder();
    foreach (string s in faillist)
    {
        fail.AppendLine(s);
    }
    Assert.Fail(fail.ToString());
}

复制代码

  这样,可以对该测试方法中包含的所有Assert断言进行统一管理。这样做有几个好处:

  • 保证程序能够正常运行
  • 对每一个测试方法而言,其中会包含多个验证方法,而每一个验证方法中又会包含有多个Assert断言,使用上述方法可以有效管理所有的断言。
  • 自定义测试报告的格式
  • 可以定义测试方法是通过或者失败。失败的测试方法通过Assert.Fail()方法来抛出异常,如果一个测试方法中没有异常抛出,则会被默认为测试成功,尽管事实上测试是失败的。

Coded UI Test如何搜索一个控件?

  在Coded UI Test中,最常见的问题是如何找到被测试的控件。只有找到被测试的对象,才能使用断言来判断其中的属性是否满足预期的值。大多数情况下,我们都会使用Coded UI Test Builder窗口来捕获UI上的控件,但有些情况下我们不得不自行搜索需要的控件。一个简单的例子,在列表控件中如何查找第一个子元素中所包含的文本。就像本文一开始给出的测试需求。如果你通过Coded UI Test Builder直接查找第一个子元素,其中生成的搜索条件往往具有特定性,当页面的条件发生变化,特定的搜索条件不一定能找到对应的控件。

  查看.Designer.cs文件中自动生成的代码,所有控件的定义都会包含类似于下面代码的搜索条件:

复制代码

HtmlEdit  mUIEmailEdit = new HtmlEdit(someAncestorControl); 
mUIEmailEdit.SearchProperties[HtmlEdit.PropertyNames.Id] = "email"; 
mUIEmailEdit.SearchProperties[HtmlEdit.PropertyNames.Name] = "email"; 
mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.LabeledBy] = null; 
mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.Type] = "SINGLELINE"; 
mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.Title] = null; 
mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.Class] = null; 
mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.ControlDefinition] = "id=email size=25 name=email"; 
mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.TagInstance] = "7";

mUIEmailEdit.Find();

复制代码

  Coded UI Test会试图通过所有已知的条件来搜索指定的控件,它使用广度优先查找方法(Breadth-First)。所有SearchProperties可以被视为使用AND条件进行查找,如果通过SearhProperties找到一个或未找到对应的控件,则所有的FilterProperties条件不会被使用。如果通过所有的SearchProperties条件找到多个对应的控件,则尝试逐个使用给出的FilterProperties条件进行按序匹配(Ordered match),直到找到匹配的控件。如果通过以上给出的所有条件最终找到多余一个的匹配项,则第一个匹配的元素即为找到的控件。

  在上面给出的例子中,会按照如下顺利进行搜索:

  1. 搜索会从someAncestorControl控件开始,因为它被作为要搜索控件的祖先。注意祖先并不一定是父节点,它可以是被搜索控件的任意上级控件。
  2. 每一次遍历搜索时,会使用所有已定义的SearchProperties条件进行筛选,假设有三个控件均满足搜索条件,如X1,X2和X3,此时会继续使用剩下的FilterProperties条件进行筛选。
  3. 对控件X1,X2和X3分别使用已定义的FilterProperties条件进行筛选,直到匹配到唯一的控件,或者所有的FilterProperties条件均比较完毕。

  下面的流程图说明了这一过程:

  有一点需要注意:

  1. 对Web controls进行搜索:SearchProperties和FilterProperties均支持
  2. 对WinForms和WPF controls进行搜索:仅SearchProperties支持

  在Web controls中,搜索条件的使用可能会涉及到浏览器兼容性问题。如筛选条件最终需要通过InnerText来确定控件,而该属性在某些浏览器上并不支持,此时可能引发异常。在程序编码过程中尝试给特定的控件指定ID属性可以更好的解决这一问题,这就需要与程序开发人员进行有效的沟通。从这一点也可以看出,测试驱动开发的重要性。

  不要尝试通过GetChildren()方法来遍历所有的控件,因为该方法返回结果会很慢,尤其是当页面中存在大量控件时。可以使用临近的祖先节点对该控件进行定义(构造函数的参数可以用来指定被搜索控件的祖先),然后通过给定SearchProperties或FilterProperties来对控件进行筛选,然后使用FindMatchingControls()方法来确定要搜索的控件。如下面的代码用来遍历Table元素从而找到表中所有的<th/>和<td/>:

复制代码

HtmlTable uITable = this.UIRelWindow.UIRelDocument.UITable;
HtmlRow rowall = new HtmlRow(uITable);

UITestControlCollection rows = rowall.FindMatchingControls();

int rowCount = rows.Count;

for (int i = 0; i < rowCount; i++)
{
    HtmlHeaderCell allTH = new HtmlHeaderCell(rows[i]);
    HtmlCell allTD = new HtmlCell(rows[i]);
    UITestControlCollection THs = allTH.FindMatchingControls();
    UITestControlCollection TDs = allTD.FindMatchingControls();
    
    ... ...
}

复制代码

代码结构调整

  .uitest文件针对的是每一个测试页面,每个页面都有单独的验证方法用来测试页面上各个不同的部分,具有良好结构的代码可以使整个测试工程看起来思路清晰。如果有必要,你完全可以使用设计模式来更加简练地组织工程中的测试方法和类。一个完好的测试工程代码结构看起来像这样:

复制代码

public class TestRunner
{
    public TestRunner()
    {
        homePage = new UI.HomePageClasses.HomePage();
    }


    #region Home page actions and validate method

    private UI.HomePageClasses.HomePage homePage;
    public UI.PageClasses.HomePage HomePage
    {
        get
        {
            if ((this.homePage == null))
            {
                this.homePage = new UI.PageClasses.HomePage();
            }
            return this.homePage;
        }
        set
        {
            homePage = value;
        }
    }

    public void LaunchHomePage()
    {
        HomePage.LaunchHomePage(new System.Uri(TestSettings.GetCurrentSiteURL()));
    }
    public void ValidateHomePageText()
    {
        HomePage.ValidateHomePageText();
    }
}

复制代码

  使用TestRunner类将工程中所有的验证方法和UI Actions方法进行包装,然后在测试方法中进行调用。

复制代码

[TestMethod]
public void IncomeStatementsTest()
{
    testrunner.NavigateToTestPage();
    testrunner.ValidateSomething();
}

[TestInitialize()]
public void MyTestInitialize()
{
    testrunner = new TestRunner();
    testrunner.LaunchHomePage();
}

Use TestCleanup to run code after each test has run
[TestCleanup()]
public void MyTestCleanup()
{
    testrunner = null;
}

private TestRunner testrunner;

复制代码

  忘记说明一点,带有[CodedUITest]特征属性的类中,我们可以借用MyTestInitialize()方法和MyTestCleanup()方法进行一些初始化操作和清理工作。不要在该类的构造函数中添加任何代码,通过带有[TestInitialize]特征属性的方法进行初始化工作。同样,带有[TestCleanup]特征属性的方法可以用来进行一些清理工作。

  另外,和大多数工程一样,Coded UI Test工程允许使用App.config文件。在工程中添加该文件并加入<appSettings></appSettings>节点以设置配置信息。

<configuration>
  <appSettings>
    <add key ="" value=""/>
  </appSettings>
</configuration>

如何使用命令行方式运行测试方法?

  除了在Visual Studio中运行测试方法外,我们还可以通过其它许多方式来运行测试方法。使用测试代理和测试控制器可以对所有的测试方法进行有效管理,并可以将测试方法分发到不同的测试机上单独进行测试,但需要在服务器上进行部署,MSDN上有相应的介绍,这里主要介绍如何通过命令行方式来运行测试方法。

  1. 编译Coded UI Test工程,将生成的.dll文件复制到指定的目录下。
  2. 打开Windows命令行窗口,转到目录c:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE
    或者直接打开Developer Command Prompt for VS2012
  3. 使用如下命令行执行测试方法
MSTest /testcontainer:CodedUITestProject2.dll /test:CodedUITest1.CodedUITestMethod1

  msdn上有对MSTest.exe命令行所有参数的说明。有几点需要说明一下:

  • /testcontainer中有必要指定.dll文件的路径名称
  • /test用来指定.dll文件中所包含的测试方法的全名称。注意这里的全名称包含了测试方法所在的类名以及方法名,类必须包含[CodedUITest]特征属性,测试方法必须包含[TestMethod]特征属性。

  如果你想分发你的测试工程在其它机器上运行,可以编写.bat文件并将Coded UI Test工程生成的.dll文件放到同一文件夹下。.bat文件的内容看起来像下面这样:

复制代码

@echo off

@set PATH=c:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE;%PATH%

echo ****** This program will start a Coded UI Test Method ******
pause

MSTest /testcontainer:CodedUITest1.dll /test:CodedUITest1.CodedUITestMethod1

echo ****** End Coded UI Test Method ********


pause

复制代码

最后我邀请你进入我们的【软件测试学习交流群:785128166】, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路

作为一个软件测试的过来人,我想尽自己最大的努力,帮助每一个伙伴都能顺利找到工作。所以我整理了下面这份资源,现在免费分享给大家,有需要的小伙伴可以关注【公众号:程序员二黑】自提!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/400207.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024 年了,如何 0 基础开始学习 Vue ?

最近 5 个月&#xff0c;我都在忙着构建我的第一开源项目 HexoPress&#xff0c;这个项目是使用 Electron Vue 3 TypeScript 等技术实现的&#xff0c;一方面&#xff0c;我真的很需要一款合自己心意的博客编辑器&#xff0c;另一方面&#xff0c;我也是真心想学习 Electron …

App Inventor 2 Activity启动器技巧:如何查看并启动其他App

App包名和类名的查看 由 App Inventor 2 创建的应用要弄清包名和类名&#xff0c;可通过下载其应用程序的源代码&#xff0c;然后使用文件资源管理器或解压缩程序解压 .aia源文件&#xff08;文件的扩展名修改成.zip 或.rar&#xff0c;然后解压&#xff09;&#xff0c;在解压…

Redis面试题及核心知识点讲解

redis是单线程还是多线程&#xff1f; redis是单线程的&#xff0c;主要是指网络I/O线程。Redis的持久化&#xff0c;集群同步等操作&#xff0c;则由另外的线程来执行。 2 . redis是单线程&#xff0c;为什么处理这么快&#xff1f; Redis的大部分操作都是在内存中完成。单线…

电脑进水无法开机怎么办 电脑进水开不了机的解决方法

意外总是会不定时打破你的计划&#xff0c;电脑这类电器最怕遇到的除了火还有水&#xff0c;设备进水会导致数据丢失&#xff0c;那么我们遇到电脑进水怎么办&#xff1f;进水之后不正确处理也会引起很多不必要的麻烦. 解决办法 第一步&#xff1a;关机 如果您的电脑是在开…

山西电力市场日前价格预测【2024-02-22】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-22&#xff09;山西电力市场全天平均日前电价为441.87元/MWh。其中&#xff0c;最高日前电价为680.73元/MWh&#xff0c;预计出现在18:45。最低日前电价为352.02元/MWh&#xff0c;预计…

【git 使用】git 中head、工作树、和索引分别是什么,有什么关系和区别

HEAD 定义&#xff1a;HEAD 是指向当前所在分支&#xff08;或者是某个特定的提交&#xff09;的指针&#xff0c;它表示当前工作目录正在处于哪个提交或分支上。作用&#xff1a;HEAD 指示了当前工作目录的状态&#xff0c;可以通过 HEAD 来确定当前处于哪个分支上&#xff0…

正交匹配追踪算法(Orthogonal Matching Pursuit)实现过程及Python模拟

正交匹配追踪&#xff08;Orthogonal Matching Pursuit&#xff0c;OMP&#xff09;是一种用于寻找稀疏信号的贪婪算法&#xff0c;用于求解压缩感知问题中的稀疏近似问题。在压缩感知的背景下&#xff0c;通常我们有一个欠定的线性系统Ax y&#xff0c;其中A是一个已知的测量…

IDEA实现ssh远程连接本地Linux服务器

文章目录 1. 检查Linux SSH服务2. 本地连接测试3. Linux 安装Cpolar4. 创建远程连接公网地址5. 公网远程连接测试6. 固定连接公网地址7. 固定地址连接测试 本文主要介绍如何在IDEA中设置远程连接服务器开发环境&#xff0c;并结合Cpolar内网穿透工具实现无公网远程连接&#xf…

IAR推出新版IAR Embedded Workbench for Arm功能安全版,该版本配备经过认证的静态代码分析功能

瑞典乌普萨拉&#xff0c;2024年2月20日 – 全球领先的嵌入式系统开发软件解决方案供应商IAR宣布&#xff1a;推出其旗舰产品IAR Embedded Workbench for Arm功能安全版的最新版本9.50.3。此次发布进一步加强了IAR支持开发人员创建安全、可靠和符合标准的嵌入式应用程序的承诺&…

回归分析中的异方差性

在简单线性回归或多元线性回归中&#xff0c;我们对误差项做了一些基本假设。 简单线性回归&#xff1a; 多元线性回归&#xff1a; 假设条件&#xff1a; 1.误差均值为零 2.误差具有恒定方差 3.误差不相关 4.误差呈正态分布 第2个假设称为同方差性&#xff0c;因此&…

QML | 信号和信号处理器特性

信号和信号处理器特性 很多时候,应用程序的用户界面组件需要相互通信。例如,一个按钮需要知道用户是否进行了单击:当用户单击后,它可能会更改颜色来指示它状态的改变,或者执行一些逻辑代码实现一定的功能。同Qt一样,QML包含了相似的信号和信号处理器机制。 信号是发出事件…

逻辑回归为什么使用交叉熵而不用均方差?

逻辑回归为什么使用交叉熵而不用均方差&#xff1f;或者说逻辑回归的损失函数为什么不用最小二乘&#xff1f; 下面主要从两个角度进行阐述&#xff1a; 从逻辑回归的角度出发&#xff0c;逻辑回归的预测值是一个概率&#xff0c;而交叉熵又表示真实概率分布与预测概率分布的…

(C++) 详解内存地址空间

详解内存空间 0. 概述 一个C/C 程序&#xff0c;编译之后&#xff0c;形成的程序&#xff0c;在执行期间&#xff0c;内存中不仅存在一块区域用于存放代码&#xff0c;还有一些其他的区域用于使用&#xff0c;本节会详解C/C内部所使用的内存地址空间&#xff0c;关于各内存的…

每日OJ题_二叉树dfs③_力扣814. 二叉树剪枝

目录 力扣814. 二叉树剪枝 解析代码 力扣814. 二叉树剪枝 814. 二叉树剪枝 难度 中等 给你二叉树的根结点 root &#xff0c;此外树的每个结点的值要么是 0 &#xff0c;要么是 1 。 返回移除了所有不包含 1 的子树的原二叉树。 节点 node 的子树为 node 本身加上所有 n…

深入探索Selenium自动化测试:基础、代码实战与最佳实践【第90篇—Selenium自动化测试】

文章目录 深入探索Selenium自动化测试&#xff1a;基础、代码实战与最佳实践什么是Selenium&#xff1f;安装Selenium基础用法启动浏览器查找元素操作元素 代码实战&#xff1a;模拟登录进阶用法等待元素出现处理弹窗执行JavaScript 高级应用&#xff1a;Selenium Grid启动Sele…

压缩感知常用的测量矩阵

测量矩阵的基本概念 在压缩感知&#xff08;Compressed Sensing&#xff0c;CS&#xff09;理论中&#xff0c;测量矩阵&#xff08;也称为采样矩阵&#xff09;是实现信号压缩采样的关键工具。它是一个通常为非方阵的矩阵&#xff0c;用于将信号从高维空间映射到低维空间&…

基于vue框架的环保知识普及平台设计与实现

项目&#xff1a;基于vue框架的环保知识普及平台设计与实现 目 录 摘要 I Abstract II 第1章 引言 1 1.1 研究的背景 1 1.2 目的和意义 1 1.3 设计思路 1 1.4 研究的主要内容 2 第2章 相关原理和技术 3 2.1 B/S 模式体系结构 3 2.2 Springboot技术 4 2.3 访问数据库…

“中国国安部紧急警告”!境外公司利用加密货币诱使人员非法采集空间数据!当心不慎成“帮凶”!

随着加密货币的普及&#xff0c;每天都有新的区块链项目出现&#xff0c;目前市场上已经有成千上万种不同的加密货币 一些项目可能因其名人光环、运作机制、出色的代币经济学、或是提供优良的服务而受到市场亲睐&#xff0c;但也有很多项目缺乏大众的关注&#xff0c;或是尚未有…

机器学习——强化学习作业

作业内容 成功降落在两个黄色旗子中间为成功&#xff0c;其他为失败 Policy Gradient方法 Actor-Critic方法 范例结果 baseline Policy Gradient实现

初阶数据结构之---顺序表和链表(C语言)

引言-线性表 线性表&#xff1a; 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。线性表在逻辑上是线性结构&#xff0c;也就是说是连续的一条直线。但在物理上并不一定是连续的。线性表在物理上…