Arquitetura da Solução
A aplicação Sistema Login QA foi implantada em ambiente com terminação SSL segura (https://qa.sauer.pro.br), operando com containerização Docker, backend em microframework Flask e persistência relacional em SQLite.
Diagrama de Fluxo e Topologia de Rede
Apache JMeter
Postman / cURL
Encaminhamento de Portas
app.py (Porta 5000)banco.db (SQLite)
| Camada / Módulo | Tecnologia | Endereçamento / Arquivo | Função no Sistema |
|---|---|---|---|
| Ambiente de Produção (Online) | HTTPS / TLS 1.3 | https://qa.sauer.pro.br | Servidor em nuvem acessível publicamente para a realização dos testes de QA. |
| Interface Web (Frontend) | HTML5 + Tailwind CSS + Jinja2 | templates/login.html | Interface responsiva com mensagens flash para feedback de autenticação. |
| Servidor de Aplicação (Backend) | Python 3.11 / Flask | app.py | Processa requisições HTTP, valida credenciais e gerencia rotas. |
| Banco de Dados Relacional | SQLite 3 | banco.db | Armazena tabela usuarios e usuário padrão admin / 123456. |
| Containerização | Docker & Docker Compose | Dockerfile / docker-compose.yml | Isola a aplicação mapeando internamente a porta 5555:5000. |
- Ambiente Online Oficial (Recomendado): https://qa.sauer.pro.br (Protocolo:
https| Servidor:qa.sauer.pro.br| Porta:443) - Ambiente Local (Docker Compose):
http://localhost:5555 - Ambiente Local (Python direto):
http://localhost:5000
APIs e Rotas da Solução
Abaixo estão documentadas todas as rotas HTTP da aplicação disponíveis no servidor https://qa.sauer.pro.br.
/
Descrição:
Retorna o formulário web com a tela de login estilizada.
Respostas Possíveis:
templates/login.html contendo o título Sistema Login QA.
Exemplo cURL (Online):
curl -X GET https://qa.sauer.pro.br/
/login
Descrição:
Processa o login do usuário via dados de formulário (application/x-www-form-urlencoded) e consulta a tabela no banco SQLite.
Parâmetros do Corpo (Form Body):
| Campo | Tipo | Obrigatório? | Credencial Padrão de Teste |
|---|---|---|---|
| usuario | String | Sim | admin |
| senha | String | Sim | 123456 |
Respostas Possíveis:
<h1>Bem-vindo, admin! Login realizado com sucesso.</h1>.
"Usuário ou senha inválidos." e redireciona para /.
Exemplos cURL (Online):
# 1. Testar Login com Sucesso
curl -X POST https://qa.sauer.pro.br/login -d "usuario=admin&senha=123456"
# 2. Testar Login com Senha Incorreta
curl -X POST https://qa.sauer.pro.br/login -d "usuario=admin&senha=senha_errada"
/documentacao
Retorna este portal interativo com as instruções passo a passo, catálogo de APIs e laboratórios de teste de carga/stress.
Exercício 01: Testes de Carga e Stress com Apache JMeter
Laboratório Prático Passo a Passo (Ambiente https://qa.sauer.pro.br)🎯 Conceito Fundamental: Carga vs. Stress
Para que você não confunda os dois testes durante a atividade, veja a metáfora prática abaixo:
Metáfora do Elevador: Se um elevador suporta 8 pessoas, colocamos 5 a 8 pessoas para subir e medimos se ele chega ao andar no tempo prometido.
https://qa.sauer.pro.br/login. A resposta deve ser rápida (< 300ms) e com 0.00% de erros.
Metáfora do Elevador: Colocamos 30 pessoas espremidas até o alarme disparar ou o elevador parar de responder.
Passo 0: Instalação, Mini Curso e Execução do JMeter
Passo a passo ilustrado para baixar o Java, o arquivo ZIP do JMeter e descompactar.
Mini curso detalhado explicando cada menu, componente e como interpretar os resultados no nosso laboratório.
- Abra a pasta onde você descompactou o Apache JMeter (ex:
apache-jmeter-5.6.3). - Entre na pasta
bin. - No Windows: Dê duplo clique no arquivo
jmeter.bat(ou no Linux/Mac execute./jmeter.shno terminal). - Aguarde alguns segundos e a interface gráfica do JMeter será exibida.
Passo 1: Criar o Grupo de Usuários Virtuais (Thread Group)
Cada Thread representa um usuário virtual simulando acessos reais ao site https://qa.sauer.pro.br.
- Clique com o botão direito em
Test Plan(Plano de Teste). - Passe o mouse sobre Add ➔ Threads (Users) ➔ Clique em Thread Group.
- No campo Name, digite:
Grupo_Usuarios_QA.
| Campo na Tela | O que significa? | Valor para TESTE DE CARGA | Valor para TESTE DE STRESS |
|---|---|---|---|
| Number of Threads (users) | Número de pessoas virtuais disparando requisições. | 20 | 300 a 500 |
| Ramp-up period (seconds) | Tempo em segundos para que todas as threads entrem em ação de forma escalonada. | 5 | 10 |
| Loop Count | Quantas vezes seguidas cada usuário repetirá o teste. | 5 | 10 |
Passo 2: Configurar a Requisição HTTP Apontando para https://qa.sauer.pro.br
Configure o JMeter para chamar a rota de autenticação segura (POST /login).
- Clique com o botão direito em
Grupo_Usuarios_QA. - Passe o mouse em Add ➔ Sampler ➔ Clique em HTTP Request.
- Preencha os campos exatamente como abaixo:
Passo 3: Validar a Resposta com Asserção (Response Assertion)
A asserção garante que o JMeter validará automaticamente se a frase de boas-vindas foi retornada no corpo da página.
- Clique com o botão direito em cima de
POST /login (qa.sauer.pro.br). - Passe o mouse em Add ➔ Assertions ➔ Clique em Response Assertion.
- Em Field to Test, deixe marcado Text Response.
- Em Pattern Matching Rules, marque Substring.
- Na caixa Patterns to Test, clique no botão Add e digite:
Bem-vindo, admin!
Passo 4: Adicionar Ouvintes para Visualizar Métricas (Listeners)
Adicione os dois principais ouvintes para acompanhar os resultados em tempo real:
Exibe cada requisição individual com sinal verde (sucesso) ou vermelho (erro) e permite inspecionar o HTML retornado.
Gera as métricas estatísticas consolidadas: Média de latência (ms), % de erro e vazão de requisições por segundo.
Passo 5: Executando o Teste e Preenchendo o Relatório de QA
Para iniciar o teste no JMeter, clique no botão verde com ícone de reprodução ▶ (Start) no topo. Para limpar os dados antes de uma nova rodada, clique no ícone de duas vassouras 🧹 Clear All.
| Coluna no Summary Report | O que você observará no TESTE DE CARGA | O que você observará no TESTE DE STRESS | Conclusão de QA |
|---|---|---|---|
| Samples (Amostras) | 100 (20 threads × 5 loops) | 5.000 (500 threads × 10 loops) | Total de requisições enviadas ao servidor. |
| Average (Tempo Médio) | Baixo (ex: 20ms a 150ms) | Aumenta para 2.000ms a 10.000ms+ | No stress o servidor enfileira os acessos, gerando lentidão visível. |
| Error % (Taxa de Erros) | 0.00% | Aparecem erros (10% a 50%+) | Sobrecarga gera Gateway Timeout (504), Bad Gateway (502) ou lock no SQLite. |
| Throughput (Vazão) | Vazão estável em req/seg | Atinge teto e depois despenca | Identifica a capacidade máxima de processamento do hardware. |
Plano de Testes JMX Pré-configurado
Pronto para https://qa.sauer.pro.br
Copie o arquivo XML abaixo ou utilize o arquivo teste_login_jmeter.jmx na raiz do projeto e abra diretamente no JMeter em File ➔ Open.
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Plano de Testes - https://qa.sauer.pro.br" enabled="true">
<stringProp name="TestPlan.comments">Laboratorio de Carga e Stress - Cotemig QA</stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="Variaveis do Usuario" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Grupo_Usuarios_QA" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Controlador de Loop" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">5</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">20</stringProp>
<stringProp name="ThreadGroup.ramp_time">5</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST /login (qa.sauer.pro.br)" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variaveis" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="usuario" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.name">usuario</stringProp>
<stringProp name="Argument.value">admin</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
</elementProp>
<elementProp name="senha" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.name">senha</stringProp>
<stringProp name="Argument.value">123456</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">qa.sauer.pro.br</stringProp>
<stringProp name="HTTPSampler.port">443</stringProp>
<stringProp name="HTTPSampler.protocol">https</stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">/login</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
</HTTPSamplerProxy>
<hashTree>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Validar Boas-vindas" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="49586">Bem-vindo, admin!</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">2</intProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
Perguntas de Fixação & Gabarito
Teste seus conhecimentos1. Qual é a principal diferença entre Teste de Carga e Teste de Stress? ▼
2. Ao testar um servidor online como https://qa.sauer.pro.br no JMeter, quais campos de conexão devem ser preenchidos? ▼
Protocol = https, Server Name or IP = qa.sauer.pro.br, Port = 443 (ou em branco), Method = POST e Path = /login. Não se deve colocar "https://" dentro do campo Server Name.
3. No JMeter, o que significa o campo "Ramp-up Period"? ▼
4. Para que serve o componente "Response Assertion" no JMeter? ▼
Bem-vindo, admin!). Se a frase não existir (ex: falha de login), o JMeter marca a requisição como erro (vermelha).