Substituindo métodos de set-up por utilitários de teste

You are viewing an old revision of this post, from October 26, 2023 @ 10:05:40. See below for differences between this version and the current revision.

(Este é um post que publiquei no antigo blog da SEA Tecnologia. Como ainda é relevante, resolvi republicá-lo.)

E-Plamtax foi um projeto muito positivo que executamos para a Força Aérea. A equipe do E-Plamtax aprendeu muitas coisas no projeto, mas muitas dessas aprendizagens ficaram guardadas conosco. Uma das mais interessantes é a criação de utilitários de teste.

Utilitários de teste são uma maneira de reaproveitar código em testes unitários. Usualmente, isso é feito utilizando os métodos setUp ou @Before dos casos de teste, mas isso tem algumas desvantagens. Por exemplo, em um caso de teste, podemos ter a seguinte inicialização:

private Address address;
private AddressDAO addressDAO;

@Before
public void setUp() {
    address = new Address();
    address.setStreet("Rua fulano");
    address.setNumber("123/A");
    addressDAO = new AddressDAO();
}Code language: PHP (php)

Essa inicialização funciona bem no teste abaixo…

@Test
public void testGetAllAddresses(){
    addressDAO.addAddress(address);

    List<Address> addresses = addressDAO.getAllAddresses();

    assertEquals(1, addresses.size());
    assertEquals("Rua fulano", addresses.get(0).getStreet());
    assertEquals("123/A", addresses.get(0).getNumber());
} Code language: PHP (php)

Agora, se tivermos o teste a seguir, o objeto criado é desperdiçado:

@Test
public void testGetNoAddress() {
    List<Address> addresses = addressDAO.getAllAddresses();

    assertEquals(0, addresses.size());
}Code language: PHP (php)

Se o código for como o seguinte, teremos redundância de código. também temos de decidir SE o outro objeto deve ser criado no @Before também ou no método.

@Test
public void testGetAllAddressesMoreThanOne() {
    addressDAO.addAddress(address);
    Address address2 = new Address();
    address2.setStreet("Outra rua");
    address2.setNumber("111");
    addressDAO.addAddress(address2);
    List<Address> addresses = addressDAO.getAllAddresses(); 
    assertEquals(1, addresses.size());
    assertEquals("Rua fulano", addresses.get(0).getStreet());
    assertEquals("123/A", addresses.get(0).getNumber()); 
}Code language: PHP (php)

Esses inconvenientes são menores quando comparados à tarefa de criar uma rede de dependências. Por exemplo, para testar uma classe Person que agrega um Address em um outro caso de teste, teremos de ter um @Before semelhante a esse:

private Person person;
private Address address;
private PersonDAO personDAO;

@Before     
public void setUp() {
    address = new Address();
    address.setStreet("Rua fulano");
    address.setNumber("123/A");
    person = new Person();
    person.setName("João");
    person.setAddress(address);
    personDAO = new PersonDAO();
} Code language: PHP (php)

O código para a criação de endereços foi duplicado, e é difícil criar as dependências. Nesses exemplos, vemos casos simples, mas é fácil visualizar como a situação irá se complicar.

Nós solucionamos esse problema criando uma classe para criar esses objetos. Essa classe seria algo como isso:

public class TestUtil {
    public static Address utilCreateAddress(String street, String number) {
        Address address = new Address();
        address.setStreet("Rua fulano");
        address.setNumber("123/A");
        return address;     
    }

    public static Person utilCreatePerson(String name, Address address) {
        Person person = new Person();
        person.setName(name);
        person.setAddress(address);
        return person;
    }
}Code language: JavaScript (javascript)

Nossos casos de teste estendiam a TestUtil, facilitando a criação de objetos:

public class TestAddress2 extends TestUtil {
    private AddressDAO addressDAO = new AddressDAO();

    @Test
    public void testGetAllAddresses() {
        Address address = utilCreateAddress("Rua fulano", "123/A");
        addressDAO.addAddress(address);

        List<Address> addresses = addressDAO.getAllAddresses();

        assertEquals(1, addresses.size());
        assertEquals("Rua fulano", addresses.get(0).getStreet());
        assertEquals("123/A", addresses.get(0).getNumber());
    }

    @Test
    public void testGetNoAddress() {
        List<Address> addresses = addressDAO.getAllAddresses();

        assertEquals(0, addresses.size());
    }

    @Test
    public void testGetAllAddressesMoreThanOne() {
        Address address = utilCreateAddress("Rua fulano", "123/A");
        Address address2 = utilCreateAddress("Outra rua", "111");
        addressDAO.addAddress(address);
        addressDAO.addAddress(address2);

        List<Address> addresses = addressDAO.getAllAddresses();

        assertEquals(2, addresses.size());
        assertEquals("Rua fulano", addresses.get(0).getStreet());
        assertEquals("123/A", addresses.get(0).getNumber());
    } 
} Code language: PHP (php)

Como também precisávamos frequentemente de um objeto qualquer, ou que apenas um ou outro parâmetro fosse definido, criávamos variantes dos métodos:

public static Address utilCreateAddress() {
    return utilCreateAddress("Qualquer", "Qualquer");
}

public static Person utilCreatePerson() {
    return utilCreatePerson("José", utilCreateAddress());
} Code language: PHP (php)

O E-Plamtax foi um projeto um tanto complexo, com grandes redes de dependências de objetos. O uso desses utilitários de teste viabilizou a prática de TDD no sistema. Era emocionante descobrir que, para criar aquele documento que dependia de sete outros documentos e uns cinco ou seis usuários, bastava chamar um método.

Naturalmente, há mais sobre nossos utilitários de teste do que foi escrito aqui, e pode haver mais ainda que sequer fizemos. (Por exemplo, pode ser interessante produzir utilitários de teste para classes específicas, ao invés de um gigantesco utilitário) Entretanto, como a ideia é bem simples, esperamos que esse pontapé inicial lhe motive a pensar sobre o tema. Até mais!

Post Revisions:

Changes:

October 26, 2023 @ 10:05:40Current Revision
Content
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p><em>(Este é um post que publiquei no <a href="https:/ /web.archive.org/ web/20100806130712/ http://blog.seatecnologia.com.br: 80/2010/05/20/ utilitirios- de-teste">antigo blog da SEA Tecnologia</a>. Como ainda é relevante, resolvi republicá-lo.)</em></p>Unchanged: <p><em>(Este é um post que publiquei no <a href="https:/ /web.archive.org/ web/20100806130712/ http://blog.seatecnologia.com.br: 80/2010/05/20/ utilitirios- de-teste">antigo blog da SEA Tecnologia</a>. Como ainda é relevante, resolvi republicá-lo.)</em></p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Deleted: <p>E-Plamtax foi um projeto muito positivo que executamos para a Força Aérea. A equipe do E-Plamtax aprendeu muitas coisas no projeto, mas muitas dessas aprendizagens ficaram guardadas conosco. Uma das mais interessantes é a criação de <em>utilitários de teste</em>.</p> Added: <p>Uma das muitas aprendizagens que adquiri na antiga SEA Tecnologia é a criação de <em>utilitários de teste</em>.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Utilitários de teste são uma maneira de reaproveitar código em testes unitários. Usualmente, isso é feito utilizando os métodos <code>setUp</code> ou <code>@Before</code> dos casos de teste, mas isso tem algumas desvantagens. Por exemplo, em um caso de teste, podemos ter a seguinte inicialização:</p>Unchanged: <p>Utilitários de teste são uma maneira de reaproveitar código em testes unitários. Usualmente, isso é feito utilizando os métodos <code>setUp</code> ou <code>@Before</code> dos casos de teste, mas isso tem algumas desvantagens. Por exemplo, em um caso de teste, podemos ter a seguinte inicialização:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code -->Unchanged: <!-- wp:code -->
Unchanged: <pre class="wp-block- code"><code>private Address address;Unchanged: <pre class="wp-block- code"><code>private Address address;
Unchanged: private AddressDAO addressDAO;Unchanged: private AddressDAO addressDAO;
Unchanged: @BeforeUnchanged: @Before
Unchanged: public void setUp() {Unchanged: public void setUp() {
Unchanged: address = new Address();Unchanged: address = new Address();
Unchanged: address.setStreet("Rua fulano");Unchanged: address.setStreet("Rua fulano");
Unchanged: address.setNumber("123/A");Unchanged: address.setNumber("123/A");
Unchanged: addressDAO = new AddressDAO();Unchanged: addressDAO = new AddressDAO();
Unchanged: }</code></pre>Unchanged: }</code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Essa inicialização funciona bem no teste abaixo...</p>Unchanged: <p>Essa inicialização funciona bem no teste abaixo...</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code -->Unchanged: <!-- wp:code -->
Unchanged: <pre class="wp-block- code"><code>@TestUnchanged: <pre class="wp-block- code"><code>@Test
Unchanged: public void testGetAllAddresses(){Unchanged: public void testGetAllAddresses(){
Unchanged: addressDAO.addAddress(address);Unchanged: addressDAO.addAddress(address);
Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();
Unchanged: assertEquals(1, addresses.size());Unchanged: assertEquals(1, addresses.size());
Unchanged: assertEquals("Rua fulano", addresses.get( 0).getStreet());Unchanged: assertEquals("Rua fulano", addresses.get( 0).getStreet());
Unchanged: assertEquals("123/A", addresses.get( 0).getNumber());Unchanged: assertEquals("123/A", addresses.get( 0).getNumber());
Unchanged: } </code></pre>Unchanged: } </code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Agora, se tivermos o teste a seguir, o objeto criado é desperdiçado:</p>Unchanged: <p>Agora, se tivermos o teste a seguir, o objeto criado é desperdiçado:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code -->Unchanged: <!-- wp:code -->
Unchanged: <pre class="wp-block- code"><code>@TestUnchanged: <pre class="wp-block- code"><code>@Test
Unchanged: public void testGetNoAddress() {Unchanged: public void testGetNoAddress() {
Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();
Unchanged: assertEquals(0, addresses.size());Unchanged: assertEquals(0, addresses.size());
Unchanged: }</code></pre>Unchanged: }</code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Se o código for como o seguinte, teremos redundância de código. também temos de decidir SE o outro objeto deve ser criado no <code>@Before</code> também ou no método.</p>Unchanged: <p>Se o código for como o seguinte, teremos redundância de código. também temos de decidir SE o outro objeto deve ser criado no <code>@Before</code> também ou no método.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code -->Unchanged: <!-- wp:code -->
Unchanged: <pre class="wp-block- code"><code>@TestUnchanged: <pre class="wp-block- code"><code>@Test
Unchanged: public void testGetAllAddressesMoreThanOne() {Unchanged: public void testGetAllAddressesMoreThanOne() {
Unchanged: addressDAO.addAddress(address);Unchanged: addressDAO.addAddress(address);
Unchanged: Address address2 = new Address();Unchanged: Address address2 = new Address();
Unchanged: address2.setStreet("Outra rua");Unchanged: address2.setStreet("Outra rua");
Unchanged: address2.setNumber("111");Unchanged: address2.setNumber("111");
Unchanged: addressDAO.addAddress(address2);Unchanged: addressDAO.addAddress(address2);
Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses(); Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();
Unchanged: assertEquals(1, addresses.size());Unchanged: assertEquals(1, addresses.size());
Unchanged: assertEquals("Rua fulano", addresses.get( 0).getStreet());Unchanged: assertEquals("Rua fulano", addresses.get( 0).getStreet());
Unchanged: assertEquals("123/A", addresses.get( 0).getNumber()); Unchanged: assertEquals("123/A", addresses.get( 0).getNumber());
Unchanged: }</code></pre>Unchanged: }</code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Esses inconvenientes são menores quando comparados à tarefa de criar uma rede de dependências. Por exemplo, para testar uma classe <code>Person</code> que agrega um <code>Address</code> em um outro caso de teste, teremos de ter um <code>@Before</code> semelhante a esse:</p>Unchanged: <p>Esses inconvenientes são menores quando comparados à tarefa de criar uma rede de dependências. Por exemplo, para testar uma classe <code>Person</code> que agrega um <code>Address</code> em um outro caso de teste, teremos de ter um <code>@Before</code> semelhante a esse:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code -->Unchanged: <!-- wp:code -->
Unchanged: <pre class="wp-block- code"><code>private Person person;Unchanged: <pre class="wp-block- code"><code>private Person person;
Unchanged: private Address address;Unchanged: private Address address;
Unchanged: private PersonDAO personDAO;Unchanged: private PersonDAO personDAO;
Unchanged: @Before Unchanged: @Before
Unchanged: public void setUp() {Unchanged: public void setUp() {
Unchanged: address = new Address();Unchanged: address = new Address();
Unchanged: address.setStreet("Rua fulano");Unchanged: address.setStreet("Rua fulano");
Unchanged: address.setNumber("123/A");Unchanged: address.setNumber("123/A");
Unchanged: person = new Person();Unchanged: person = new Person();
Unchanged: person.setName("João");Unchanged: person.setName("João");
Unchanged: person.setAddress(address);Unchanged: person.setAddress(address);
Unchanged: personDAO = new PersonDAO();Unchanged: personDAO = new PersonDAO();
Unchanged: } </code></pre>Unchanged: } </code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>O código para a criação de endereços foi duplicado, e é difícil criar as dependências. Nesses exemplos, vemos casos simples, mas é fácil visualizar como a situação irá se complicar.</p>Unchanged: <p>O código para a criação de endereços foi duplicado, e é difícil criar as dependências. Nesses exemplos, vemos casos simples, mas é fácil visualizar como a situação irá se complicar.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Nós solucionamos esse problema criando uma classe para criar esses objetos. Essa classe seria algo como isso:</p>Unchanged: <p>Nós solucionamos esse problema criando uma classe para criar esses objetos. Essa classe seria algo como isso:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code -->Unchanged: <!-- wp:code -->
Unchanged: <pre class="wp-block- code"><code>public class TestUtil {Unchanged: <pre class="wp-block- code"><code>public class TestUtil {
Unchanged: public static Address utilCreateAddress(String street, String number) {Unchanged: public static Address utilCreateAddress(String street, String number) {
Unchanged: Address address = new Address();Unchanged: Address address = new Address();
Unchanged: address.setStreet("Rua fulano");Unchanged: address.setStreet("Rua fulano");
Unchanged: address.setNumber("123/A");Unchanged: address.setNumber("123/A");
Unchanged: return address; Unchanged: return address;
Unchanged: }Unchanged: }
Unchanged: public static Person utilCreatePerson(String name, Address address) {Unchanged: public static Person utilCreatePerson(String name, Address address) {
Unchanged: Person person = new Person();Unchanged: Person person = new Person();
Unchanged: person.setName(name);Unchanged: person.setName(name);
Unchanged: person.setAddress(address);Unchanged: person.setAddress(address);
Unchanged: return person;Unchanged: return person;
Unchanged: }Unchanged: }
Unchanged: }</code></pre>Unchanged: }</code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Nossos casos de teste estendiam a <code>TestUtil</code>, facilitando a criação de objetos:</p>Unchanged: <p>Nossos casos de teste estendiam a <code>TestUtil</code>, facilitando a criação de objetos:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code -->Unchanged: <!-- wp:code -->
Unchanged: <pre class="wp-block- code"><code>public class TestAddress2 extends TestUtil {Unchanged: <pre class="wp-block- code"><code>public class TestAddress2 extends TestUtil {
Unchanged: private AddressDAO addressDAO = new AddressDAO();Unchanged: private AddressDAO addressDAO = new AddressDAO();
Unchanged: @TestUnchanged: @Test
Unchanged: public void testGetAllAddresses() {Unchanged: public void testGetAllAddresses() {
Unchanged: Address address = utilCreateAddress("Rua fulano", "123/A");Unchanged: Address address = utilCreateAddress("Rua fulano", "123/A");
Unchanged: addressDAO.addAddress(address);Unchanged: addressDAO.addAddress(address);
Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();
Unchanged: assertEquals(1, addresses.size());Unchanged: assertEquals(1, addresses.size());
Unchanged: assertEquals("Rua fulano", addresses.get( 0).getStreet());Unchanged: assertEquals("Rua fulano", addresses.get( 0).getStreet());
Unchanged: assertEquals("123/A", addresses.get( 0).getNumber());Unchanged: assertEquals("123/A", addresses.get( 0).getNumber());
Unchanged: }Unchanged: }
Unchanged: @TestUnchanged: @Test
Unchanged: public void testGetNoAddress() {Unchanged: public void testGetNoAddress() {
Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();
Unchanged: assertEquals(0, addresses.size());Unchanged: assertEquals(0, addresses.size());
Unchanged: }Unchanged: }
Unchanged: @TestUnchanged: @Test
Unchanged: public void testGetAllAddressesMoreThanOne() {Unchanged: public void testGetAllAddressesMoreThanOne() {
Unchanged: Address address = utilCreateAddress("Rua fulano", "123/A");Unchanged: Address address = utilCreateAddress("Rua fulano", "123/A");
Unchanged: Address address2 = utilCreateAddress("Outra rua", "111");Unchanged: Address address2 = utilCreateAddress("Outra rua", "111");
Unchanged: addressDAO.addAddress(address);Unchanged: addressDAO.addAddress(address);
Unchanged: addressDAO.addAddress(address2);Unchanged: addressDAO.addAddress(address2);
Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();Unchanged: List&lt;Address&gt; addresses = addressDAO.getAllAddresses();
Unchanged: assertEquals(2, addresses.size());Unchanged: assertEquals(2, addresses.size());
Unchanged: assertEquals("Rua fulano", addresses.get( 0).getStreet());Unchanged: assertEquals("Rua fulano", addresses.get( 0).getStreet());
Unchanged: assertEquals("123/A", addresses.get( 0).getNumber());Unchanged: assertEquals("123/A", addresses.get( 0).getNumber());
Unchanged: } Unchanged: }
Unchanged: } </code></pre>Unchanged: } </code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Como também precisávamos frequentemente de um objeto qualquer, ou que apenas um ou outro parâmetro fosse definido, criávamos variantes dos métodos:</p>Unchanged: <p>Como também precisávamos frequentemente de um objeto qualquer, ou que apenas um ou outro parâmetro fosse definido, criávamos variantes dos métodos:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code -->Unchanged: <!-- wp:code -->
Unchanged: <pre class="wp-block- code"><code>public static Address utilCreateAddress() {Unchanged: <pre class="wp-block- code"><code>public static Address utilCreateAddress() {
Unchanged: return utilCreateAddress("Qualquer", "Qualquer");Unchanged: return utilCreateAddress("Qualquer", "Qualquer");
Unchanged: }Unchanged: }
Unchanged: public static Person utilCreatePerson() {Unchanged: public static Person utilCreatePerson() {
Unchanged: return utilCreatePerson("José", utilCreateAddress());Unchanged: return utilCreatePerson("José", utilCreateAddress());
Unchanged: } </code></pre>Unchanged: } </code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Deleted: <p>O E-Plamtax foi um projeto um tanto complexo, com grandes redes de dependências de objetos. O uso desses utilitários de teste viabilizou a prática de <a href="https:/ /web.archive.org/ web/20100806130712/ http://pt.wikipedia.org/wiki/ Test_Driven_Development">TDD</a> no sistema. Era emocionante descobrir que, para criar aquele documento que dependia de sete outros documentos e uns cinco ou seis usuários, bastava chamar um método.</p> Added: <p>Aprendemos isto em um projeto um tanto complexo, com grandes redes de dependências de objetos. O uso desses utilitários de teste viabilizou a prática de <a href="https:/ /web.archive.org/ web/20100806130712/ http://pt.wikipedia.org/wiki/ Test_Driven_Development">TDD</a> no sistema. Era emocionante descobrir que, para criar aquele documento que dependia de sete outros documentos e uns cinco ou seis usuários, bastava chamar um método.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Naturalmente, há mais sobre nossos utilitários de teste do que foi escrito aqui, e pode haver mais ainda que sequer fizemos. (Por exemplo, pode ser interessante produzir utilitários de teste para classes específicas, ao invés de um gigantesco utilitário) Entretanto, como a ideia é bem simples, esperamos que esse pontapé inicial lhe motive a pensar sobre o tema. Até mais!</p>Unchanged: <p>Naturalmente, há mais sobre nossos utilitários de teste do que foi escrito aqui, e pode haver mais ainda que sequer fizemos. (Por exemplo, pode ser interessante produzir utilitários de teste para classes específicas, ao invés de um gigantesco utilitário) Entretanto, como a ideia é bem simples, esperamos que esse pontapé inicial lhe motive a pensar sobre o tema. Até mais!</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->

Note: Spaces may be added to comparison text to allow better line wrapping.

Post a Comment

Your email is never shared. Required fields are marked *

*
*

%d bloggers like this: