Skip to content

9. A aplicação [SimuPaie] – versão 5 – ASP.NET / serviço web


Leituras recomendadas: referência [2], Introdução ao C# 2008, capítulo 10 “Serviços da Web”


9.1. A nova arquitetura da aplicação

A arquitetura em camadas da aplicação Pam é atualmente a seguinte:

Vamos evoluí-la da seguinte forma:

Enquanto na arquitetura anterior as camadas [web], [metier] e [dao] eram executadas na mesma máquina virtual,NET, na nova arquitetura, a camada [web] será executada em uma máquina virtual diferente daquelas em que as camadas [metier] e [dao] são executadas. Esse será o caso, especialmente, se a camada [web] estiver em uma máquina M1 e as camadas [metier] e [dao] estiverem em uma máquina M2. Temos aqui uma arquitetura cliente/servidor:

  • o servidor é composto pelas camadas [metier] e [dao]. Por se tratar de um serviço web, ele precisa do servidor web nº 2 para ser executado.
  • O cliente é composto pela camada [web]. Para ser executado, ele precisa do servidor web nº 1.
  • O cliente e o servidor se comunicam pela rede TCP/IP com o protocolo HTTP / SOAP. Para isso, duas novas camadas devem ser adicionadas à arquitetura:
    • a camada [S], que será um serviço web. O serviço web recebe as solicitações dos clientes remotos e utiliza as camadas [metier] e [dao] para atendê-las. Existem diversas maneiras de construir um serviço TCP/IP. A vantagem do serviço web é dupla:
      • ele utiliza o protocolo HTTP, que é permitido pelos firewalls de empresas e órgãos públicos
      • ele utiliza um subprotocolo HTTP / SOAP padrão, implementado por diversas plataformas de desenvolvimento: .Net, Java, PHP, Flex, ... Assim, um serviço web pode ser “consumido” (esse é o termo comum) por clientes .Net, Java, PHP, Flex, etc.
    • a camada [C], que será o cliente do serviço web remoto. Sua função será se comunicar com o serviço web [S].

Essa nova arquitetura pode ser derivada das anteriores sem muito esforço:

  • as camadas [metier] e [dao] permanecem inalteradas
  • a camada [web] sofre uma ligeira evolução, essencialmente para referenciar entidades como Employe e FeuilleSalaire, que passaram a ser entidades da camada cliente [C]. Essas entidades são análogas às das camadas [metier] ou [dao], mas pertencem a espaços de nomes diferentes.
  • A camada de servidor [S] é uma classe que implementa a interface IPamMetier da camada [metier]. Essa implementação limita-se a chamar os métodos correspondentes da camada [metier]. Os métodos implementados pela camada de servidor [S] serão “expostos” aos clientes remotos, que poderão chamá-los.
  • A camada cliente [C] será gerada pelo Visual Studio.

Os princípios da nova arquitetura são os seguintes:

  • a camada [web] continua a se comunicar com a camada [metier] como se esta fosse local. Para isso, a camada cliente [C] implementa ainterface IPamMetier da camada [metier] real e se apresenta à camada [web] como uma camada [metier] local. Além do problema dos espaços de nomes mencionado anteriormente, a camada [web] permanece inalterada. Essa é a vantagem de se trabalhar em camadas. Se tivéssemos construído um aplicativo de camada única, seria necessário reformulá-lo profundamente.
  • A camada cliente [C] transmite, de forma transparente para a camada [web], as solicitações desta última ao serviço web remoto [S]. Ela é responsável por todo o aspecto de “comunicação de rede”. Recebe uma resposta do serviço web remoto, que ela formata para entregá-la à camada [web] no formato esperado por esta.
  • No lado do servidor, o serviço web [S] recebe comandos de seus clientes remotos. Ele os formata para chamar os métodos da interface IPamMetier da camada [metier]. Ao receber a resposta da camada [metier], ele a formata para transmiti-la pela rede ao cliente [C]. As camadas [metier] e [dao] não precisam ser modificadas.

9.2. O projeto do Visual Web Developer para o serviço web

Estamos criando um novo projeto com o Visual Web Developer:

  • em [1], selecionamos um projeto web em C#
  • em [2], selecionamos “Aplicativo de serviço web ASP.NET”
  • em [3], nomeamos o projeto web
  • em [4], indicamos um local para esse projeto
  • em [1], o projeto gerado. Trata-se de um projeto web clássico, com as seguintes particularidades:
    • foi especificado que o projeto é do tipo “serviço web”. Um serviço web não envia páginas da web HTML aos seus clientes, mas sim dados no formato XML. Portanto, a página [Default.aspx], que normalmente seria gerada, não foi criada.
  • Em [2], foi gerado um arquivo [Service1.asmx] com o seguinte conteúdo:

<%@ WebService Language="C#" CodeBehind="Service1.asmx.cs" Class="pam_v5_webservice.Service1" %>
  • (continuação)
    • - a tag WebService indica que [Service.asmx] é um serviço web
    • - o atributo CodeBehind indica a localização do código-fonte desse serviço web
    • - o atributo Class indica o nome da classe que implementa o serviço web no código-fonte

O código-fonte [Service.asmx.cs] do serviço web gerado por padrão é o seguinte:


using System.Web.Services;

namespace pam_v5_webservice
{
  /// <summary>
  /// Descrição resumida do Serviço1
  /// </summary>
  [WebService(Namespace = "http://tempuri.org/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  // Para permitir a chamada deste serviço web a partir de um script usando ASP.NET AJAX, remova os símbolos de comentário da linha a seguir. 
  // [System.Web.Script.Services.ScriptService]
  public class Service1 : System.Web.Services.WebService
  {

    [WebMethod]
    public string HelloWorld()
    {
      return "Hello World";
    }
  }
}
  • linha 8: a anotação WebService, que faz com que a classe Service1 da linha 13 seja exposta como um serviço web. Um serviço web pertence a um espaço de nomes para evitar que dois serviços web em todo o mundo tenham o mesmo nome. Teremos que alterar esse espaço de nomes posteriormente.
  • linha 13: a classe Service1 deriva da classe WebService do framework .NET.
  • linha 16: a anotação WebMethod faz com que o método assim anotado seja exposto aos clientes remotos, que poderão, assim, chamá-lo.
  • linhas 17-20: o método HelloWorld é um método de demonstração. Nós o excluiremos mais tarde. Ele nos permite realizar os primeiros testes e conhecer as ferramentas do Visual Studio, bem como alguns aspectos importantes sobre serviços web.
  • no [1], executamos o serviço web [Service.asmx]
  • VS O Web Developer iniciou seu servidor web integrado e o configurou para escutar em uma porta aleatória, neste caso, a 1599. A URL [2] foi então solicitada ao servidor web. Trata-se da URL de uma página de teste do serviço web.
  • em [3], um link que permite visualizar o arquivo de descrição do serviço web. Esse arquivo, chamado WSDL (WebService Description Language) devido à sua extensão (.wsdl), é um arquivo XML que descreve os métodos expostos pelo serviço web. É a partir desse arquivo WSDL que os clientes podem conhecer:
    • o namespace do serviço web
    • a lista de métodos expostos pelo serviço web
    • os parâmetros esperados por cada um deles
    • a resposta retornada por cada um deles
  • no [4], o único método exposto pelo serviço web .
  • em [5], o conteúdo do arquivo WSDL obtido por meio do link [3]. Destaque para o URL e o [6]. É necessário que os usuários do serviço web tenham conhecimento desses arquivos.
  • em [7], a página obtida ao acessar o link [4] permite chamar o método [HelloWorld] do serviço web
  • em [8], o resultado obtido: uma resposta XML. Observe-se o URL [9] do método.

A análise das páginas anteriores permite compreender como um método de um serviço web é chamado e que tipo de resposta ele retorna. Isso permite escrever clientes HTTP capazes de interagir com o serviço web. A maioria dos IDE atuais permite a geração automática desse cliente HTTP, poupando assim o desenvolvedor de ter que escrevê-lo. Esse é o caso, notadamente, do Visual Studio Express.

Antes de prosseguir com este projeto, vamos alterar o namespace utilizado por padrão durante a geração das classes:

Ao selecionarmos as propriedades do projeto (clique com o botão direito do mouse no projeto / Propriedades), vemos em [1] o nome do projeto e em [2] seu namespace padrão.

Feito isso,

  • em [Service1.asmx.cs], alteramos o namespace da classe:

using System.Web.Services;

namespace pam_v5
{
...
  public class Service1 : System.Web.Services.WebService
  {
...
  }
}
  • em [Service.asmx], alteramos também o namespace utilizado para a classe [Service1] (clique com o botão direito do mouse / exibir marcação):

<%@ WebService Language="C#" CodeBehind="Service1.asmx.cs" Class="pam_v5.Service1" %>

Voltemos à arquitetura do nosso aplicativo:

  • a camada [S] é o serviço web. Ela se limita a expor os métodos da camada [metier] a clientes remotos. É essa camada que estamos construindo.
  • A camada [C] é o cliente HTTP do serviço web. É essa camada que os IDE sabem gerar automaticamente.
  • A camada [web] vê a camada [C] como uma camada [metier] local, desde que a camada [C] implemente ainterface da camada remota [metier].

Vemos a seguir que nosso serviço web irá:

  • expor os métodos da camada [metier]
  • interagir com esta última, que, por sua vez, irá interagir com a camada [dao].

O projeto deve, portanto, utilizar os DLL das camadas [metier] e [dao]. Ele evolui da seguinte forma:

  • para [1], adicionam-se referências ao projeto
  • em [2], selecionam-se as DLL habituais da pasta [lib]. Deve-se certificar-se de que todas elas tenham a propriedade “Cópia local” definida como True. Os DLL selecionados são aqueles que implementam as camadas [metier] e [dao] com suporte NHibernate.

Um aplicativo web do tipo “serviço web ASP.NET” pode ter uma classe de aplicativo global “Global.asax”, assim como um aplicativo “site web ASP.NET” convencional. Vimos a vantagem dessa classe:

  • ela é instanciada ao iniciar a aplicação e permanece na memória
  • assim, pode armazenar dados compartilhados por todos os clientes e que são somente para leitura. Em nossa aplicação, ela armazenará, assim como nas anteriores, a lista simplificada de funcionários. Isso evitará a necessidade de buscar essa lista no banco de dados quando um cliente a solicitar.
  • em [1], clique com o botão direito do mouse no projeto
  • em [2], selecione a opção [Ajouter un nouvel élément]
  • em [3], selecione [Classe d'application globale]
  • em [4], o arquivo [Global.asax] foi adicionado ao projeto

O conteúdo do arquivo [Global.asax] é o seguinte:


<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v5.Global" Language="C#" %>

O conteúdo do arquivo [Global.asax.cs] é o seguinte:


using System;

namespace pam_v5
{
  public class Global : System.Web.HttpApplication
  {

    protected void Application_Start(object sender, EventArgs e)
    {

    }
...
  }
}

O que devemos fazer no método Application_Start? Exatamente a mesma coisa que nas aplicações web anteriores. Voltemos à arquitetura da aplicação e posicionemos nela a classe [Global]:

No esquema acima,

  • a classe [Global] é instanciada quando o serviço web é iniciado. Ela permanece na memória enquanto o serviço web estiver ativo.
  • A classe [Global] instancia as camadas [metier] e [dao] em seu método [Application_Start]
  • Para melhorar o desempenho, a classe [Global] armazena a lista simplificada de funcionários em um campo interno. Ela fornecerá a lista de funcionários a partir desse campo.
  • O serviço web, por sua vez, é instanciado a cada solicitação de um cliente. Ele é encerrado após atender a solicitação. Ele não se comunicará diretamente com a camada [metier], mas sim com a classe [Global]. Esta implementará a interface da camada [metier].

A classe [Global] é análoga àquela já criada para as aplicações anteriores:


using System;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace pam_v5
{
  public class Global : System.Web.HttpApplication
  {
    // --- dados estáticos do aplicativo ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;

    protected void Application_Start(object sender, EventArgs e)
    {
      // instanciação da camada [metier]
      PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
      // recuperamos a tabela simplificada dos funcionários 
      Employes = PamMetier.GetAllIdentitesEmployes();
    }

    // lista simplificada de funcionários
    static public Employe[] GetAllIdentitesEmployes()
    {
      return Employes;
    }

    // salário de um funcionário
    static public FeuilleSalaire GetSalaire(string SS, double heuresTravaillées, int joursTravailles)
    {
      return PamMetier.GetSalaire(SS, heuresTravaillées, joursTravailles);
    }
  }
}

A classe [Global] implementa a interface [IPamMetier], mas isso não é explicitado na declaração:


  public class Global : System.Web.HttpApplication, IPamMetier

De fato, os métodos GetAllIdentitesEmployes (linha 24) e GetSalaire (linha 30) são estáticos, enquanto os métodos da interface IPamMetier não o são. Portanto, a classe Global não pode implementar a interface IPamMetier. Além disso, não é possível declarar os métodos GetAllIdentitesEmployes e GetSalaire como não estáticos. Na verdade, eles são acessados por meio do nome da classe e não por meio de uma instância dela.

  • linha 15: o método Application_Start é semelhante ao das classes [Global] analisadas nas versões anteriores. Ele instancia a camada [metier] (linha 18) e, em seguida, inicializa (linha 20) a matriz de funcionários da linha 12.
  • linha 24: o método GetAllIdentitesEmployes limita-se a retornar a matriz de funcionários da linha 12. É por isso que vale a pena armazená-la na memória logo no início da aplicação.
  • linha 30: o método GetSalaire chama o método de mesmo nome da camada [metier].

Para instanciar a camada [metier] (linha 18), a classe [Global] utiliza o framework Spring. Este framework é configurado pelo arquivo [Web.config], que é idêntico ao do projeto anterior: ele configura o Spring e o NHibernate para instanciar as camadas [metier] e [dao] do serviço web.

Voltemos à arquitetura de nossa aplicação cliente/servidor:

No lado do servidor, resta apenas escrever o próprio serviço web [S]. Se voltarmos à arquitetura da aplicação:

vemos que, no lado do servidor, todas as camadas que precedem a camada [metier] implementam a interface desta última, IPamMetier. Isso não é obrigatório, mas é uma abordagem que parece lógica. Esse raciocínio pode ser aplicado no lado do cliente, ao cliente [C] do serviço web [S]. Assim, todas as camadas que separam a camada [web] da camada [metier] implementam, então, a interface IPamMetier. Pode-se dizer, portanto, que voltamos a uma aplicação de três camadas:

  • a camada de apresentação [web] [1]
  • a camada [metier] [2]
  • a camada de acesso aos dados [3]

A implementação do serviço web [Service1.asmx.cs] poderia ser a seguinte:


using System.Web.Services;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;

namespace pam_v5
{
  [WebService(Namespace = "http://st.istia.univ-angers.fr/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  public class Service1 : System.Web.Services.WebService, IPamMetier
  {

    // lista de todas as identidades dos funcionários 
    [WebMethod]
    public Employe[] GetAllIdentitesEmployes()
    {
      return Global.GetAllIdentitesEmployes();
    }

    // ------- o cálculo do salário 
    [WebMethod]
    public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles)
    {
      return Global.GetSalaire(ss, heuresTravaillees, joursTravailles);
    }
  }
}
  • linha 8: a classe é anotada com o atributo [WebService] e atribuímos um nome ao espaço de nomes do serviço web
  • linha 11: a classe [Service1] herda da classe [WebService] e implementa a interface [IPamMetier]
  • linhas 15 e 22: cada método da classe é anotado com o atributo [WebMethod] para ser exposto aos clientes remotos. Por padrão, todos os métodos públicos de um serviço web são expostos. Os atributos das linhas 15 e 22 são, portanto, opcionais neste caso. Para implementar a interface [IPamMetier], cada método se limita a chamar o método de mesmo nome da classe [Global].

Estamos prontos para a execução do serviço web:

  • em [1], o projeto é regenerado
  • em [2], seleciona-se o serviço web [Service1.asmx] e exibe-se no navegador [3]
  • em [4], a página da web exibida. Ela apresenta os métodos do serviço web.
  • em [4], seguimos o link [GetAllIdentitesEmployes] e obtemos, em [5], a página de teste desse método.
  • em [6], o URL do método
  • em [7], o botão [Appeler] que permite testar o método. Este não requer nenhum parâmetro.
  • em [8], o resultado XML retornado pelo serviço web. Nele, apenas as propriedades SS, Nom, Prenom dos objetos Employe são relevantes, pois o método [GetAllIdentitesEmployes] solicita apenas essas propriedades. No entanto, esse método retorna uma matriz de objetos Employe. Observa-se em [8] que as propriedades numéricas Id e Version estão no fluxo XML retornado, mas não as propriedades com valor null: Adresse, Ville, CodePostal, Indemnites.

Temos um serviço web ativo. Agora vamos criar um cliente em C# para ele. Para isso, precisaremos do URI do arquivo WSDL do serviço web. Nós o obtemos na página exibida inicialmente ao executar o [Service.asmx]:

  • no [1], o URI do serviço web
  • em [2], o link que leva ao seu arquivo WSDL
  • em [3], o valor desse link

9.3. O projeto em C# de um cliente NUnit do serviço web

Criamos um projeto em C# (com o Visual C# e não com o Visual Web Developer) para o cliente do serviço web. Será um cliente de teste NUnit. Portanto, o projeto será do tipo “Biblioteca de classes”.

  • no [1], criamos um projeto em C# do tipo “Biblioteca de classes”
  • em [2], nomeamos o projeto
  • em [3], o projeto. Excluímos [Class1.cs].
  • em [4], o novo projeto.
  • nas propriedades do projeto, na aba [Application] [5], definimos o namespace do projeto. Cada classe gerada pelo IDE será criada nesse namespace.

Salvamos nosso novo projeto em um local de nossa preferência:

 

Feito isso, geramos o cliente do serviço web remoto. Para entender o que vamos fazer, é preciso voltar à arquitetura cliente/servidor que estamos construindo:

O IDE irá gerar a camada cliente [C] a partir do URI do arquivo WSDL do serviço web [S]. Vale lembrar que o URI desse arquivo já havia sido registrado anteriormente. Procedemos a partir do da seguinte maneira:

  • para [1], clicar com o botão direito do mouse no ramo References e adicionar uma referência de serviço
  • em [2], indique o URL do arquivo WSDL do serviço web anotado anteriormente. Este deve ser iniciado previamente, caso ainda não esteja em execução.
  • em [3], solicitar a descoberta do serviço web por meio de seu arquivo WSDL
  • no [4], o serviço web descoberto
  • em [5], os métodos expostos pelo serviço web.
  • em [6], o espaço de nomes no qual se deseja colocar as classes e interfaces do cliente que será gerado.
  • confirma-se o assistente

  • em [1], o cliente gerado. Clique duas vezes nele para acessar seu conteúdo.
  • Em [2], no explorador de objetos, são exibidas as classes e interfaces do espaço de nomes Client.WsPam. Esse é o espaço de nomes do cliente gerado.
  • Em [3], a classe que implementa o cliente do serviço web.
  • Em [4], os métodos implementados pelo cliente [Service1SoapClient]. Nele, encontram-se os dois métodos do serviço web remoto [5] e [6].
  • Em [2], encontram-se as imagens das entidades das camadas:
    • [metier]: FeuilleSalaire, ElementsSalaire
    • [dao]: Employe, Cotisations, Indemnites

A seguir, é importante lembrar que essas imagens das entidades remotas estão localizadas no lado do cliente e no espaço de nomes PamV5Client.WsPam.

Vamos examinar os métodos e propriedades expostos por uma delas:

  • em [1], seleciona-se a classe local [Employe]
  • em [2], encontramos as propriedades da entidade remota [Employe], bem como campos privados utilizados para as necessidades específicas da entidade local.

Voltemos à nossa aplicação em C#. Adicionamos a ela uma classe de teste NUnit:

  • Na classe [1], foi adicionada a classe [NUnit]. A classe [NUnit] precisará do framework NUnit e, portanto, de uma referência à classe DLL deste framework. Supomos aqui que o framework NUnit tenha sido instalado no computador (http://nunit.org/).
  • No [2], adiciona-se uma referência ao projeto
  • na aba [3]. No NET, que reúne os DLL registrados no computador, selecionamos o [4], o DLL e o [nunit.framework], versão mínima 2.4.6.

Além disso, vamos usar o Spring para instanciar o cliente local [C] do serviço web [S]:

A referência ao DLL do Spring pode ser adicionada da mesma forma que foi feito com o framework NUnit, caso os DLL tenham sido previamente registrados no computador (http://www.springframework.net/download.html).

Procedemos de maneira diferente. Utilizamos a pasta [lib] dos projetos anteriores, que continha os DLL necessários para o Spring, e adicionamos a referência do Spring ao projeto:

Voltemos à arquitetura do cliente que está sendo desenvolvido:

Acima, vemos que o cliente de teste [1] se conecta a uma camada [metier] [2] estendida. Essa camada apresenta os mesmos métodos que a camada remota [metier]. Portanto, podemos utilizar a classe de teste já apresentada durante o teste da camada [metier] no projeto C# [pam-metier-dao-nhibernate]:


using NUnit.Framework;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace Pam.Metier.Tests {

    [TestFixture()]
    public class NunitTestPamMetier : AssertionHelper {

        // a camada [metier] a ser testada 
        private IPamMetier pamMetier;

        // construtor
        public NunitTestPamMetier() {
            // instanciação da camada [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }


        [Test]
        public void GetAllIdentitesEmployes() {
            // verificação do número de funcionários 
            Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetSalaire1() {
            // cálculo de uma folha de pagamento 
            FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
            // verificações 
            Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
            // folha de pagamento de um funcionário inexistente 
            bool erreur = false;
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (PamException) {
                erreur = true;
            }
            Expect(erreur, True);
        }

    }
}

Há algumas alterações a serem feitas:

  • na linha 18, instanciamos a camada [metier] com o framework Spring. A classe não é a mesma nos dois casos. Aqui, a camada local [metier] é uma instância da classe [PamV5Client.WsPam.Service1SoapClient], a classe gerada pelo IDE. Portanto, o Spring está configurado da seguinte maneira no arquivo [app.config] do projeto C#:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>

    <spring>
        <context>
            <resource uri="config://spring/objects" />
        </context>
        <objects xmlns="http://www.springframework.net">
            <object id="pammetier" type="PamV5Client.WsPam.Service1SoapClient, pam-v5-client-csharp-webservice"/>
        </objects>
    </spring>


    <system.serviceModel>
...
  • na linha 16 acima, o objeto [pammetier] é uma instância da classe [PamV5Client.WsPam.Service1SoapClient], que se encontra no assembly [pam-v5-client-csharp-webservice]. Para obter a primeira informação, basta voltar à definição da classe [Service1SoapClient] no explorador de objetos (parágrafo 9.3):
  • em [2], a classe de implementação da camada local [metier] e em [1] seu espaço de nomes
  • em [3]; nas propriedades do projeto, o nome do assembly, a segunda informação necessária para a configuração do objeto Spring [pammetier].

Voltemos ao código de instanciação da camada local [metier] em [NUnit.cs]:


        // a camada [metier] a ser testada 
        private IPamMetier pamMetier;

        // construtor
        public NunitTestPamMetier() {
            // instanciação da camada [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }

Na linha 7, a camada remota [metier] era do tipo IPamMetier. Aqui, a camada [metier] é do tipo [Service1SoapClient]:


public class Service1SoapClient : System.ServiceModel.ClientBase<Service1Soap>

Vemos que a classe Service1SoapClient não implementa a interface IPamMetier, mesmo que exponha métodos com o mesmo nome. Portanto, precisamos escrever a instanciação da camada local [metier] da seguinte maneira:


        // a camada [metier] a ser testada 
        private Service1SoapClient pamMetier;

        // construtor
        public NunitTestPamMetier() {
            // instanciação da camada [metier]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
}

Outra modificação a ser feita:


        [Test]
        public void GetSalaire1() {
...
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (PamException) {
                erreur = true;
            }
            Expect(erreur, True);
        }

O código acima utiliza, na linha 6, o tipo PamException, que não existe no lado do cliente. Vamos substituí-lo por sua classe pai, o tipo Exception.


        [Test]
        public void GetSalaire1() {
...
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (Exception) {
                erreur = true;
            }
            Expect(erreur, True);
        }

Por fim, os namespaces importados não são mais os mesmos:


using System;
using PamV5Client.WsPam;
using NUnit.Framework;
using Spring.Context.Support;

Feito isso, o projeto do tipo “Biblioteca de classes” pode ser gerado. É criado o seguinte DLL:

  • em [1], a pasta [bin/Release] do projeto C#
  • em [2], o DLL do projeto.

O teste NUnit é então executado pelo framework NUnit (a base MySQL dbpam_nhibernate deve estar ativa para o teste):

  • em [3] e [4], a DLL [2] é carregada no aplicativo de teste NUnit
  • no [5], a classe de teste é selecionada e executada [6]
  • em [7], os resultados de um teste bem-sucedido

Agora temos um serviço web operacional.