Esse artigo faz parte da revista .NET Magazine edição 53. Clique aqui para ler todos os artigos desta edição

e="FONT-SIZE: 10pt; COLOR: windowtext; FONT-FAMILY: Verdana; mso-no-proof: no; mso-ansi-language: PT-BR">Projeto/Análise - Expert

Testes Unitários

Mocks e Stubs – Parte 1

 

Neste artigo veremos

·       Testes unitários;

·       Mocks e Stubs;

·       .Net 3.5, Visual Studio 2008 e VB 9.0;

·       Rhino Mocks.

Qual a finalidade

·         Explicar o conceito de testes unitários com mocks e stubs.

Quais situações utilizam esses recursos?

·         Criação de testes unitários para quaisquer projetos que envolvam uma boa separação de responsabilidades.

 

Resumo do DevMan

Testes unitários são importantes para garantir a qualidade do código. Neste artigo, aprenda a fazer testes unitários com Mocks e Stubs, e entre em contato com conceitos de OO como injeção de dependência e separação de responsabilidades.

 

Falar de testes unitários é sempre interessante e tem ficado ainda mais interessante recentemente. A comunidade de desenvolvimento e as empresas têm se esforçado para criar soluções melhores, e novas abordagens têm surgido a cada dia.

Quando falamos de testes unitários logo vêm à mente termos como TDD, BDD, Stubs, Mocks, entre diversos outros. Ferramentas também surgem freqüentemente para apoiar todos esses nomes e siglas.

Neste artigo veremos porque usar testes unitários, como criar uma aplicação testável, e como utilizar ferramentas que nos auxiliem nos testes. Começaremos com uma aplicação simples, do tipo que vemos todos os dias, e vamos buscar entender porque a abordagem mais comum no desenvolvimento de sistemas traz problemas graves na hora de aplicarmos testes. Veremos também como alguns padrões de projeto (Design Patterns) vão nos ajudar a criar aplicações mais testáveis (falamos de Design Patterns em uma série de cinco artigos que foi da edição 47 à 51).

 

Aplicação de exemplo

Vamos montar uma aplicação de exemplo para podermos apoiar os futuros testes. A aplicação será extremamente simples. Para que possamos focar na solução de testes unitários, utilizaremos um banco de dados de exemplo da Microsoft para SQL Server 2005 e 2008 chamado AdventureWorks, disponível via Web no Codeplex no endereço http://www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=4004 . A aplicação estará separada em 6 camadas, sendo 4 projetos, gerando por isso 4 dlls, uma destas camadas sendo a de apresentação, nesse caso Web. Hoje esse tipo de interface é a mais comum (e portanto mais conhecida de todos), mas poderíamos utilizar qualquer outra.

O projeto tratará de exibir dados das tabelas SalesOrderHeader e Contact do banco AdventureWorks. A exibição será feita em um único formulário Web, conforme a Figura 1. Neste formulário, o grid exibe as colunas Order ID, Order Date e Total Due que têm a origem dos dados na tabela SalesOrderHeader, em colunas de nome semelhante, e a coluna Customer, que vêm das colunas Contact.FirstName e Contact.LastName concatenadas. Será possível paginar os dados com um engine customizado, e ir a uma página específica. O código desta página (default.aspx) e do code behind (default.aspx.vb) estão na Listagem 1. Este código serve apenas para que possamos montar a aplicação, mas não será importante para o objetivo do artigo, servindo apenas para exemplificar a interação da camada de apresentação com as outras camadas (procure a palavra-chave new e encontrará esses relacionamentos). Note que os únicos métodos de negócio chamados são os métodos GetOrders e GetQuantityOfOrders. O código da default.aspx contém somente as partes importantes, como o gridview e os outros controles, e o exemplo segue o inglês, já que os dados e os elementos do banco também estão nesta língua.

 

Figura 1. Resultado final da solução

 

Listagem 1. Arquivos default.aspx e default.aspx.vb

 

<asp:GridView ID="gvOrders" runat="server" AutoGenerateColumns="False">

    <Columns>

        <asp:BoundField DataField="SalesOrderID" HeaderText="Order ID" />

        <asp:BoundField DataField="OrderDate" HeaderText="Order Date"

            DataFormatString="{0:d}"  />

        <asp:TemplateField HeaderText="Customer">

            <ItemTemplate>

                <asp:Label ID="Label1" runat="server" Text='<%# Eval("Contact.FirstName") & " " & Eval("Contact.LastName") %>'></asp:Label>

            </ItemTemplate>

        </asp:TemplateField>

        <asp:BoundField DataField="TotalDue" HeaderText="Total Due"

            DataFormatString="{0:F}" />

    </Columns>

</asp:GridView>

<br />

<asp:LinkButton ID="lnkPrevious" runat="server">&lt;</asp:LinkButton>

&nbsp;<asp:LinkButton ID="lnkNext" runat="server">&gt;</asp:LinkButton>

&nbsp;<asp:LinkButton ID="lnkbutGoToPage" runat="server">Go to page:</asp:LinkButton>

&nbsp;<asp:TextBox ID="txtGoToPage" runat="server" MaxLength="5" Width="38px">1</asp:TextBox>

<asp:RequiredFieldValidator ID="rvGoToPage" runat="server"

    ControlToValidate="txtGoToPage" Display="Dynamic"

    ErrorMessage="Insert a Number">*</asp:RequiredFieldValidator>

<asp:CompareValidator ID="cvGoToPage" runat="server"

    ControlToValidate="txtGoToPage" Display="Dynamic"

    ErrorMessage="Insert a valid number" Operator="DataTypeCheck">*</asp:CompareValidator>

<br />

<br />

Pages:            

<asp:Label ID="lblCurrentPage" runat="server" Text="undefined"></asp:Label>

&nbsp;/             <asp:Label ID="lblPages" runat="server" Text="undefined"></asp:Label>

<asp:ValidationSummary ID="ValidationSummary1" runat="server" />       

 

Partial Public Class _Default

    Inherits System.Web.UI.Page

 

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        If Not Page.IsPostBack Then

            Dim bsOrders As New Business.Order()

            Dim intQtty = bsOrders.GetQuantityOfOrders()

            PagesQuantity = CInt(Math.Ceiling(intQtty / PageSize))

            LoadGrid()

        End If

    End Sub

 

    Property PagesQuantity() As Integer

        Get

            Return DirectCast(ViewState("PagesQuantity"), Integer)

        End Get

        Set(ByVal value As Integer)

            ViewState("PagesQuantity") = value

            lblPages.Text = Me.PagesQuantity.ToString()

        End Set

    End Property

 

    Private Sub LoadGrid()

 

        Dim bsOrders As New Business.Order()

        Dim orders = bsOrders.GetOrders(CurrentPage, PageSize)

        gvOrders.DataSource = orders

        gvOrders.DataBind()

        bsOrders.GetQuantityOfOrders()

 

    End Sub

 

    Private Const PageSize As Integer = 10

 

    Public Property CurrentPage() As Integer

        Get

            If ViewState("CurrentPage") Is Nothing Then

                Me.CurrentPage = 0

            End If

            Return DirectCast(ViewState("CurrentPage"), Integer)

        End Get

        Set(ByVal value As Integer)

            If value >= 0 Then

                If value >= PagesQuantity - 1 Then

                    ViewState("CurrentPage") = PagesQuantity - 1

 

                Else

                    ViewState("CurrentPage") = value

                End If

            Else

                ViewState("CurrentPage") = 0

            End If

            lblCurrentPage.Text = CStr(CurrentPage + 1)

        End Set

    End Property

 

    Protected Sub lnkPrevious_Click(ByVal sender As Object, ByVal e As EventArgs) Handles lnkPrevious.Click

        CurrentPage -= 1

        LoadGrid()

    End Sub

 

...

Quer ler esse conteúdo completo? Tenha acesso completo