Sistema de automação de teste de site - dados de teste hardcode no código de teste

No artigo anterior Teste orientado a dados, falamos sobre a técnica de separar os dados de teste e o código que representa as etapas do teste. Do ponto de vista do teste, espera-se que quanto mais cenários de teste possam ser cobertos, melhor, mas ao projetar e escrever código de teste automatizado, alguns dados de teste fixos podem ser projetados com antecedência para simplificar a escrita do código de teste automatizado.

A razão para isso (codificada em termos de programação) é que os dados de teste fixos geralmente já são cobertos por outros casos de teste por classes de equivalência. Considere o exemplo a seguir, supondo que você queira testar a função de comentário do artigo de um site de blog (como um jardim de blogs), por exemplo, para testar a função de desabilitar a função de comentário de um artigo ou para testar a função de o autor do artigo para deletar o comentário. De acordo com o processo normal, é definitivamente necessário codificar e publicar um artigo primeiro e, em seguida, codificar o caso de teste de função de comentário especificado. Tal processo tem as seguintes desvantagens:

1. Codificação redundante é necessária, pois o código de cada caso de teste de comentário deve incluir as etapas para publicar o artigo. Na programação, recomendamos fortemente que, desde que o código seja repetido duas vezes em lugares diferentes, é necessário considerar se deve Ele encapsula a ideia de uma função ou algo assim. Essa maneira de incluir codificação redundante é o que tentamos evitar durante o processo de teste, caso contrário, o programador pode ficar de bom humor um dia, refatorar o código e destruir a estrutura HTML de algumas páginas da web - mas do ponto de vista do usuário view Não há diferença, esse tipo de refatoração de código, como um testador, só pode seguir a refatoração de código do programador e modificar o código de teste. Naquela época, é claro, você esperaria que quanto menos mudanças melhor.

Por essa lacuna, algumas pessoas podem dizer, não é a etapa de teste de criar um blog efetivamente encapsulada em uma função, por que se diz que há redundância? Isso ocorre porque no processo de teste automatizado, os testadores executarão periodicamente (algumas equipes de desenvolvimento de software de alto nível exigem todos os dias) todos os códigos de teste escritos em lotes, o que envolve dois muito importantes para qualquer codificação de caso de teste em princípio:

1) Um caso de teste pode ser executado sozinho com sucesso, ou seja, se este caso de teste for executado sozinho, o caso de teste pode ser executado com sucesso - caso contrário, é um erro de codificação do produto (Bug). Por exemplo, você está codificando e testando um recurso para gerenciar postagens de blog, que normalmente só está disponível para usuários conectados. No entanto, talvez você tenha acabado de codificar um caso de teste de login e, quando o caso de teste terminar de ser executado, a operação de logout não será executada. Neste momento, você não pode presumir que o próximo caso de teste deve ser o caso de teste para o gerenciamento de artigos que você está codificando agora.

Porque o testador não apenas retém o direito de organizar e executar vários casos de teste arbitrariamente, mas também pode optar por executar este caso de teste sozinho - por exemplo, o programador acaba de refatorar o código da função de gerenciamento de artigos. Para economizar tempo de teste , o testador pode escolher Executar apenas os casos de teste para gerenciamento de artigos. Portanto, não coloque seu destino nas mãos dos outros. Ou seja, não acredite em nenhuma premissa que não seja aquela com a qual toda a equipe concorda.

2) Os casos de teste podem ser executados em qualquer sequência de casos de teste, portanto, o código de teste deve proteger o ambiente de teste o máximo possível. Por exemplo, você projetou um caso de teste para gerenciar as permissões do usuário. De um modo geral, apenas os administradores têm permissão para operar esta função. No entanto, talvez outro engenheiro de teste descuidado tenha codificado um caso de uso para testar a exclusão de usuários, o que acabou por excluir o administrador, e seu caso de uso foi executado logo após o caso de uso dele... Não faça aos outros o que você quer, pois Você não quer se deparar com essa situação, então você deve evitar que coisas semelhantes aconteçam antes de codificar seus próprios casos de teste.

Olhando para o design dos casos de teste de gerenciamento de comentários, vários de seus códigos de teste podem se parecer com isto:

[Método de teste]

public void BlogCommentIsDisabled()

{

    TestLibrary.UserHelper.LogOnAsAdmin();

    var blog = TestLibrary.BlogHelper.CreateBlog("Título da postagem do blog", "Conteúdo do artigo");

    // Vá para a página da web para gerenciar artigos

    TestLibrary.BlogHelper.ManageArticles();

    // Na lista de artigos da página web de gerenciamento de artigos, procure o título de

    // link do artigo para "título da postagem do blog",

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

    // E clique no link "Browse" na página da Web para abrir a página da Web para ler o artigo

    blogListItem.View();

    // comente esta postagem

    TestLibrary.BlogHelper.Comment(blog);

    // Em seguida, execute alguma verificação para determinar se a função de comentário está realmente desativada

    // ...

}

[Método de teste]

public void DeleteBlogComment()

{

    TestLibrary.UserHelper.LogOnAsAdmin();

    var blog = TestLibrary.BlogHelper.CreateBlog("Título da postagem do blog", "Conteúdo do artigo");

    // Vá para a página da web para gerenciar artigos

    TestLibrary.BlogHelper.ManageArticles();

    // Na lista de artigos da página web de gerenciamento de artigos, procure o título de

    // link do artigo para "título da postagem do blog",

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

    // E clique no link "Browse" na página da Web para abrir a página da Web para ler o artigo

    blogListItem.View();

    // comente esta postagem

    var comentário = TestLibrary.BlogHelper.Comment(blog);

    // Encontre o comentário agora, exclua o comentário e execute a verificação para confirmar

    // comentário é removido

}

Quando cada caso de teste for executado separadamente, não haverá problema, mas quando dois casos de teste forem executados juntos, o problema surgirá. Dois casos de uso criam um artigo com o mesmo nome, o que leva diretamente a resultados de teste instáveis. Para resolver esse problema, talvez alguém crie uma classe auxiliar (Helper Class) que gere aleatoriamente os títulos dos artigos. Esse tipo de codificação é muito difícil, pois você precisa garantir que o título do artigo seja sempre único (talvez considere o 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接口自动化测试进阶教程,真实模拟企业项目实战!!

Acho que você gosta

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