Sistema de automatización de pruebas de sitios web: datos de prueba de hardcode en el código de prueba

En el artículo anterior Pruebas basadas en datos, hablamos sobre la técnica de separar los datos de prueba y el código que representa los pasos de prueba. Desde el punto de vista de las pruebas, se espera que cuantos más escenarios de prueba se puedan cubrir, mejor, pero al diseñar y escribir código de prueba automatizado, algunos datos de prueba fijos se pueden diseñar de antemano para simplificar la escritura de código de prueba automatizado.

La razón de esto (codificado en términos de programación) es que los datos de prueba fijos generalmente ya están cubiertos por otros casos de prueba por clases de equivalencia. Considere el siguiente ejemplo, suponiendo que desea probar la función de comentarios de artículos de un sitio de blog (como un jardín de blogs), por ejemplo, para probar la función de deshabilitar la función de comentarios de un artículo, o para probar la función de el autor del artículo para eliminar el comentario. De acuerdo con el proceso normal, definitivamente es necesario codificar y publicar un artículo primero, y luego codificar el caso de prueba de la función de comentario especificado. Tal proceso tiene las siguientes desventajas:

1. Se requiere codificación redundante, porque el código de cada caso de prueba de comentario debe incluir los pasos para publicar el artículo. En programación, recomendamos encarecidamente que siempre que el código se repita dos veces en diferentes lugares, es necesario considerar si Encapsula la idea de una función o algo así. Esta forma de incluir codificación redundante es lo que tratamos de evitar durante el proceso de prueba, de lo contrario, el programador puede estar de buen humor un día, refactorizar el código y destruir la estructura HTML de algunas páginas web, pero desde el punto de vista del usuario. view No hay diferencia, este tipo de refactorización de código, como probador, solo puede seguir la refactorización de código del programador y modificar el código de prueba.En ese momento, por supuesto, esperaría que cuantos menos cambios mejor.

Debido a esta deficiencia, algunas personas pueden decir, ¿no está el paso de prueba de crear un blog encapsulado efectivamente en una función? ¿Por qué se dice que hay redundancia? Esto se debe a que en el proceso de pruebas automatizadas, los evaluadores ejecutarán periódicamente (algunos equipos de desarrollo de software de alto nivel lo requieren todos los días) todos los códigos de prueba escritos en lotes, lo que implica dos aspectos muy importantes para la codificación de cualquier caso de prueba en principio:

1) Un caso de prueba se puede ejecutar con éxito solo, es decir, si este caso de prueba se ejecuta solo, el caso de prueba se puede ejecutar con éxito; de lo contrario, es un error de codificación del producto (Bug). Por ejemplo, está codificando y probando una función para administrar publicaciones de blog, que normalmente solo está disponible para usuarios registrados. Sin embargo, tal vez acaba de codificar un caso de prueba de inicio de sesión, y cuando el caso de prueba termina de ejecutarse, no se realiza la operación de cierre de sesión. En este momento, no puede dar por sentado que el próximo caso de prueba debe ser el caso de prueba para la gestión de artículos que está codificando ahora.

Porque el probador no solo conserva el derecho de organizar y ejecutar múltiples casos de prueba arbitrariamente, sino que también puede optar por ejecutar este caso de prueba solo, por ejemplo, el programador acaba de refactorizar el código de la función de gestión de artículos. , el probador puede elegir Ejecutar solo los casos de prueba para la gestión de artículos. Así que no pongas tu destino en manos de otros. Es decir, no creas en ninguna premisa que no sea la premisa en la que todo el equipo está de acuerdo.

2) Los casos de prueba se pueden ejecutar en cualquier secuencia de casos de prueba, por lo que el código de prueba debe proteger el entorno de prueba tanto como sea posible. Por ejemplo, ha diseñado un caso de prueba para administrar los permisos de los usuarios. En términos generales, solo los administradores tienen permiso para operar esta función. Sin embargo, tal vez otro ingeniero de pruebas descuidado codificó un caso de uso para probar la eliminación de usuarios, lo que resultó en eliminar al administrador, y su caso de uso se ejecutó justo después de su caso de uso... No haga a los demás lo que quiere, ya que usted no desea encontrarse con esta situación, por lo que debe evitar que sucedan cosas similares antes de codificar sus propios casos de prueba.

Mirando hacia atrás en el diseño de los casos de prueba de gestión de comentarios, varios de sus códigos de prueba pueden verse así:

[Método de prueba]

Public void BlogCommentIsDisabled()

{

    TestLibrary.UserHelper.LogOnAsAdmin();

    var blog = TestLibrary.BlogHelper.CreateBlog("Título de la publicación del blog", "Contenido del artículo");

    // Ir a la página web para administrar artículos

    TestLibrary.BlogHelper.ManageArtículos();

    // En la lista de artículos de la página web de gestión de artículos, busque el título de

    // enlace del artículo para "título de la publicación del blog",

    var blogListItem = TestLibrary.BlogHelper.FindBlog(blog.Title);

    // Y haga clic en el enlace "Examinar" en la página web para abrir la página web para leer el artículo

    blogListItem.View();

    // comentar en esta publicación

    TestLibrary.BlogHelper.Comment(blog);

    // Luego realice alguna verificación para determinar que la función de comentario está deshabilitada

    // ...

}

[Método de prueba]

vacío público DeleteBlogComment ()

{

    TestLibrary.UserHelper.LogOnAsAdmin();

    var blog = TestLibrary.BlogHelper.CreateBlog("Título de la publicación del blog", "Contenido del artículo");

    // Ir a la página web para administrar artículos

    TestLibrary.BlogHelper.ManageArtículos();

    // En la lista de artículos de la página web de gestión de artículos, busque el título de

    // enlace del artículo para "título de la publicación del blog",

    var blogListItem = TestLibrary.BlogHelper.FindBlog(blog.Title);

    // Y haga clic en el enlace "Examinar" en la página web para abrir la página web para leer el artículo

    blogListItem.View();

    // comentar en esta publicación

    var comentario = TestLibrary.BlogHelper.Comment(blog);

    // Encuentre el comentario en este momento, elimine el comentario y luego realice la verificación para confirmar

    // se elimina el comentario

}

Cuando cada caso de prueba se ejecuta por separado, no habrá problema, pero cuando dos casos de prueba se ejecutan juntos, el problema surgirá: dos casos de uso crean un artículo con el mismo nombre, lo que conduce directamente a resultados de prueba inestables. Para resolver este problema, tal vez alguien cree una clase de ayuda (Helper Class) que genere títulos de artículos al azar. Este tipo de codificación es muy difícil, porque necesita asegurarse de que el título del artículo sea siempre único (¿quizás considere Guid?) .

2.         节省测试的时间,在用例中执行过多的步骤也会增加测试时间。虽然测试团队都会在晚上批量执行自动化测试用例,但是在产品开发的过程当中,测试用例通过率不能达到100%是很正常的。对于每一个失败的测试用例,测试人员都要分析失败的原因—判断是产品的缺陷导致的,还是由于测试代码本身的问题引起的。额外的测试步骤也会相应地增加测试人员分析失败的时间(一般测试人员都会重新执行一遍测试代码来找出问题原因)。

3.         增加不必要的测试用例失败,测试可以分好几块,一种是功能测试,也就是验证产品的功能是否可以正常工作;一种是压力测试,即测试产品在极端情况下的执行情况;还有其他的例如性能测试,国际化测试等等。一般来说,不同的测试都会有自己的自动化测试用例集合。如果在功能测试当中,用例代码在系统里面添加了很多冗余数据,执行的测试用例多了,必然导致网站的性能和反应速度会有所下降。而在测试代码中,一般都会在执行一步操作以后,等待一段时间—等网页的内容刷新。网站反应速度的下降,直接导致测试失败。例如本来在编写测试代码的时候,3秒钟肯定会刷新的网页,在测试执行的环境中,因为过多的冗余数据,30秒可能都打不开一个网页。当然啦,网站反应速度的下降肯定是产品代码的缺陷,但是不应该将压力测试和功能测试混合起来做。

因此,我个人建议,在测试过程中,例如前面举的评论功能的测试中,完全可以事先在网站的数据库中先创建好一篇或多篇专门用来做评论测试的文章。而每天晚上,在大规模执行自动化测试用例之前,编写一个小的脚本,将网站的数据库替换成这个基准数据库。

又比如,为了测试用户权限管理的功能,完全可以事先在网站的数据库当中先准备好一个管理员帐号,这个管理员帐号和密码可以当作一个常量,然后测试代码里都使用这个帐号来执行权限管理的测试。例如下面的代码:

public class Consts

{

    public const string TimeToWaitForPageToLoad = "30000";

    public const string AdminUserName = "administrator";         

    public const string AdminPassword = "0123456";

}         

public class UserHelper : UIHelperBase

{

    public UserHelper(TestLibrary settings)

        : base(settings)

    {

    }

    public void LogOnAsAdmin()

    {

        LogOn(TestLibrary.Consts.AdminUserName, TestLibrary.Consts.AdminPassword);

    }

    public void LogOn(string username, string password)

    {

        if (String.IsNullOrEmpty(username))

            throw new CaseErrorException(new ArgumentNullException("username"));

        if (String.IsNullOrEmpty(password))

            throw new CaseErrorException(new ArgumentNullException("password"));

        selenium.Open("/");

        Thread.Sleep(2000);

        if (selenium.IsElementPresent("link=Log On"))

        {

            selenium.Click("link=Log On");

        }

        if (selenium.IsElementPresent("link=Login"))

        {

            selenium.Click("link=Login");

        }

        selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

        selenium.Type("username", username);

        selenium.Type("password", password);

        selenium.Click("//input[@value='Log On']");

        selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

    }

}

在上面的代码中,我也把等待网页刷新的时间设置成常量。对于在测试代码中使用事先在基准数据库中准备的测试数据,需要一点编程技巧。请先看下面的代码,下面的代码是一段记录通过网页操作创建文章的代码:

public class Blog : UIHelperBase

{

    // 博客的标题

    public string Title { get; private set; }

    // 博客的超链接

    public string Permalink { get; private set; }

    // 博客的超链接文本

    public string MenuText { get; private set; }

    public string Owner { get; private set; }

    public Blog(TestLibrary settings, string title,

        string permalink, string menutext, string owner)

        : base(settings)

    {

        Title = title;

        Permalink = permalink;

        MenuText = menutext;

        Owner = owner;

    }

    // 通过网页界面的操作创建一篇新文章

    //

    // PostSetting是一个结构,包含了一篇新文章的所有元素,

    // 例如文章标题,内容等等.

    public Post CreatePost(PostSettings settings)

    {

        if (settings == null)

            throw new CaseErrorException(new ArgumentNullException("settings"));

        if (!String.IsNullOrEmpty(settings.Body))

            throw new CaseErrorException("Set post body is not implemented yet!");

        if (settings.PublishDateTime.HasValue)

            throw new CaseErrorException("PublishDateTime is not implemented yet!");

        // selenium这个变量,你可以想象成是一个正在浏览网页的网友的封装

        selenium.Open("/");

        selenium.Click("link=Admin");

        selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

        selenium.Click("link=Manage Blogs");

        selenium.WaitForPageToLoad("60000");

        selenium.Click(String.Format("link={0}", Title));

        selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

        selenium.Click("link=New Post");

        selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

        selenium.Type("Routable_Title", settings.Title);

        selenium.Type("Tags", settings.Tags);

        if (settings.Permalink != null)

            selenium.Type("Routable_Slug", settings.Permalink);

        if (settings.DisableNewComments)

            selenium.Click("CommentsActive");

        if (settings.PublishSetting == PostSettings.PublishSettings.PublishNow)

            selenium.Click("Command_PublishNow");

        else if ( settings.PublishSetting == PostSettings.PublishSettings.PublishLater )

            throw new CaseErrorException("PublishLater is not implemented yet!");

        selenium.Click("submit.Save");

        selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

        return new Post(TestSettings, settings, this);

    }

}

public class PostSettings

{

    public enum PublishSettings

    {

        SaveDraft,

        PublishNow,

        PublishLater

    }

    public string Title { get; set; }

    public string Permalink { get; set; }

    public string Body { get; set; }

    public string Tags { get; set; }

    public bool DisableNewComments { get; set; }

    public PublishSettings PublishSetting { get; set; }

    public DateTime? PublishDateTime { get; set; }

}

public class Post : UIHelperBase

{

// 当初创建文章的原始详细信息

    public PostSettings Settings { get; private set; }

    // 文章的标题 – 从网页上获取

    public string Title { get { return selenium.Read(...); } }

// 下面省略文章相关的操作若干

// ...

    public Post(TestLibrary settings, PostSettings postSettings, Blog blog)

        : base(settings)

    {

        Settings = postSettings;

        ContainerBlog = blog;

}

// 下面省略文章相关的操作若干

// ...

}

从上面的代码中,你可以观察到,Post的属性,除了Settings属性以外,其他的属性都是从网页上直接读取的—当然是假设当前网页正在显示对应的文章。因此,要将基准数据库集成到自动化测试代码中来,只要实例化一个PostSettings变量就好了。TestLibrary是 负责连接到Selenium-RC,并保存对应连接的类。下面的代码演示了这个思想:

public class TestLibrary

{

    public UserHelper UserHelper { get; private set; }

    public BlogHelper BlogHelper { get; private set; }

    public CommentHelper CommentHelper { get; private set; }

    public Blog DefaultBlog { get; private set; }

    public Post DefaultPost { get; private set; }

    public ISelenium Selenium { get; private set; }

    public string SiteUrl { get; private set; }

    public class Consts

    {

        public const string TimeToWaitForPageToLoad = "30000";

        public const string AdminUserName = "administrator";      

        public const string AdminPassword = "0123456";

    }

    public TestLibrary(ISelenium selenium)

    {

        this.UserHelper = new UserHelper(this);

        this.BlogHelper = new BlogHelper(this);

        this.CommentHelper = new CommentHelper(this);

        Selenium = selenium;

        InitialDefaultSiteDate();

    }

    private void InitialDefaultSiteDate()

    {

        DefaultBlog = new Blog(this, "Default Test Blog", "default-test-blog", "Default Test Blog", Consts.AdminUserName);

        DefaultPost = new Post(this, new PostSettings()

        {

            Title = "Default Test Post",

            Permalink = "default-test-post",

            Body = "This is for web site testing purpose.",

            Tags = "Test",

            PublishSetting = PostSettings.PublishSettings.PublishNow

        },

        DefaultBlog);

    }

}

下面是TestLibrary的完整源代码:

public class TestLibrary

{

    public UserHelper UserHelper { get; private set; }

    public BlogHelper BlogHelper { get; private set; }

    public CommentHelper CommentHelper { get; private set; }

    public Blog DefaultBlog { get; private set; }

    public Post DefaultPost { get; private set; }

    public ISelenium Selenium { get; private set; }

    public string SiteUrl { get; private set; }

    public class Consts

    {

        public const string TimeToWaitForPageToLoad = "30000";

        public const string AdminUserName = "administrator";         

        public const string ContributorUser = "Contributor1";

        public const string AuthorUser = "Author1";

        public const string ModeratorUser = "Moderator1";

        public const string EditorUser = "Editor1";

        public const string CommonPassword = "0123456";

        public const string AdminPassword = "0123456";

        public const string DefaultSeleniumHost = "localhost";

        public const int DefaultSeleniumPort = 4444;

        public const string DefaultBrowser = "*firefox";

        public const string DefaultSite = "http://localhost:30320";

    }

    public TestLibrary(ISelenium selenium)

    {

        this.UserHelper = new UserHelper(this);

        this.BlogHelper = new BlogHelper(this);

        this.CommentHelper = new CommentHelper(this);

        Selenium = selenium;

        InitialDefaultSiteDate();

    }

    private void InitialDefaultSiteDate()

    {

        DefaultBlog = new Blog(this, "Default Test Blog", "default-test-blog", "Default Test Blog", Consts.AdminUserName);

        DefaultPost = new Post(this, new PostSettings()

        {

            Title = "Default Test Post",

            Permalink = "default-test-post",

            Body = "This is for web site testing purpose.",

            Tags = "Test",

            PublishSetting = PostSettings.PublishSettings.PublishNow

        },

        DefaultBlog);

    }

    public static TestLibrary SetupTest(TestContext testContext)

    {

        if (testContext != null && testContext.DataRow != null && testContext.DataRow.Table.Columns.Contains("seleniumHost"))

        {

            return SetupTest(testContext.DataRow["seleniumHost"].ToString(),

                Int32.Parse(testContext.DataRow["seleniumPort"].ToString()),

                testContext.DataRow["browser"].ToString(),

                testContext.DataRow["site"].ToString());

        }

        else

        {

            return SetupTest(Consts.DefaultSeleniumHost, Consts.DefaultSeleniumPort,

                Consts.DefaultBrowser, Consts. DefaultSite);

        }

    }

    public static TestLibrary SetupTest(string seleniumHost, int seleniumPort,

        string browser, string site)

    {

        var selenium = new DefaultSelenium(

            seleniumHost, seleniumPort, browser, site);

        selenium.Start();

        return new TestLibrary(selenium) { SiteUrl = site };

    }

    public void Shutdown()

    {

        try

        {

            Selenium.Stop();

        }

        catch (Exception)

        {

            // Ignore errors if unable to close the browser

        }

    }

}

【整整200集】超超超详细的Python接口自动化测试进阶教程,真实模拟企业项目实战!!

Supongo que te gusta

Origin blog.csdn.net/xiao1542/article/details/132008343
Recomendado
Clasificación