Install
$ agentstack add skill-snk-devcenter-addon-studio-test ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Testes Backend - Addon Studio 2.0 (JUnit + Mockito)
Documento define como desenvolver testes automatizados backend Addon Studio 2.0 com libs de mercado.
Objetivo
- Garantir comportamento regras negocio.
- Permitir refator seguro.
- Reduzir regressao em servicos e mapeamentos.
Bibliotecas recomendadas
- JUnit 5 (
org.junit.jupiter) - Mockito 4.x (
org.mockito) — usar 4.11.0. Projeto Java 8. Mockito 5.x exige Java 11+. - AssertJ (assercoes fluentes)
mockito-inline(mock estaticoJapeSession,SessionFileetc.)
O que testar
Foque em logica de negocio isolada — classes de servico, regras em entidades, mapeamentos. Pular o que e framework / SDK.
Regra pratica:
- Testar unidade de negocio isolada.
- Nao testar framework Addon Studio em si.
- Nao acoplar teste unitario a banco real, Wildfly ou infra externa.
- Onde organizar arquivos de teste fica a criterio da arquitetura do projeto (convencao Maven/Gradle: espelhar pacote do
src/main/javaemsrc/test/java).
Padrao nome:
- Classe teste:
Test - Metodo teste:
deve_quando()
Configuracao completa (Gradle)
1. build.gradle raiz — adicionar classpath do plugin de log visual
buildscript {
repositories {
// ... repositorios existentes ...
maven {
url = uri("https://plugins.gradle.org/m2/")
}
}
dependencies {
// ... dependencias existentes ...
classpath "com.adarshr:gradle-test-logger-plugin:3.2.0"
}
}
2. /build.gradle — dependencias e configuracao
apply plugin: 'com.adarshr.test-logger'
testlogger {
theme 'mocha'
showExceptions true
showStackTraces true
showFullStackTraces false
showCauses true
slowThreshold 2000
showSummary true
showPassed true
showSkipped true
showFailed true
showStandardStreams false
showPassedStandardStreams false
showFailedStandardStreams true
logLevel 'lifecycle'
}
dependencies {
// SDK do Addon Studio no classpath de teste.
// Obrigatorio: repositorios (ex.: TdcXyzCadastroRepository) estendem JapeRepository
// do SDK da Sankhya, que so e disponibilizado para compilacao principal pelo
// plugin Gradle do Studio. Sem isso, o compilador nao resolve findByPK/save nos testes.
testImplementation 'br.com.sankhya.studio:sdk-sankhya:2+'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.2'
// mockito-core 4.x: compativel com Java 8 (5.x exige Java 11+)
testImplementation 'org.mockito:mockito-core:4.11.0'
testImplementation 'org.mockito:mockito-junit-jupiter:4.11.0'
// mockito-inline: necessario para mockar metodos estaticos (JapeSession, SessionFile etc.)
testImplementation 'org.mockito:mockito-inline:4.11.0'
testImplementation 'org.assertj:assertj-core:3.26.3'
}
test {
useJUnitPlatform()
}
Output visual test-logger tema mocha similar a Jest:
com.example.addon.ProcessarEntidadeServiceTest
✔ deveProcessar_quandoGatewayRetornarDados()
✔ deveLancarEntityNotFoundException_quandoEntidadeNaoEncontrada()
✘ deveLancarRuntimeException_quandoRepositorioFalhar()
...stack trace...
82 passing (2.9s)
1 failing
Template de teste unitario
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
class ProcessarEntidadeServiceTest {
@Mock
private EntidadeRepository entidadeRepository;
@Mock
private EntidadeGateway entidadeGateway;
@InjectMocks
private ProcessarEntidadeService service;
@Test
void deveProcessarEntidade_quandoGatewayRetornarDados() throws Exception {
// arrange
when(entidadeGateway.buscarDados()).thenReturn(Arrays.asList(new EntidadeDominio()));
// act
List resultado = service.execute();
// assert
assertThat(resultado).hasSize(1);
verify(entidadeGateway, times(1)).buscarDados();
}
}
Metodos JapeRepository lancam Exception verificada
JapeRepository (base de todos os repositorios) declara:
V findByPK(K pk) throws Exception;
V save(V entity) throws Exception;
Metodos consulta customizados em interfaces repositorio (ex.: findByCodParceiro, findByIdOrigem) tambem podem declarar throws Exception.
Regra: qualquer metodo teste que chame when(repository.metodo(...)) nesses metodos precisa declarar throws Exception:
@Test
void deveLancarEntityNotFoundException_quandoNaoEncontrado() throws Exception {
when(repository.findByPK(99)).thenReturn(null); // findByPK throws Exception
assertThrows(EntityNotFoundException.class, () -> service.execute(99));
}
Mocking estatico (JapeSession, SessionFile)
Use mockito-inline para mockar metodos estaticos do SDK:
import org.mockito.MockedStatic;
import static org.mockito.Mockito.mockStatic;
@Test
void deveExecutar_quandoSessionPropertyAtiva() throws Exception {
try (MockedStatic japeSessionMock = mockStatic(JapeSession.class)) {
japeSessionMock.when(() -> JapeSession.getPropertyAsBoolean(anyString(), anyBoolean()))
.thenReturn(false);
// act e assert
}
}
Use try-with-resources pra garantir mock estatico fechado apos teste.
Teste de controller (orquestracao)
Controller so orquestra — nao contem logica de negocio (ver skill controller). Teste verifica delegacao pra service e mapper:
@ExtendWith(MockitoExtension.class)
class PedidoControllerTest {
@Mock
private CriarPedidoService criarPedidoService;
@Mock
private PedidoRestMapper mapper;
@InjectMocks
private PedidoController controller;
@Test
void deveDelegarParaServiceEMapper_quandoCriarPedido() {
// arrange
CriarPedidoRequest request = new CriarPedidoRequest();
Pedido pedido = new Pedido();
PedidoResponse response = new PedidoResponse();
when(mapper.toPedido(request)).thenReturn(pedido);
when(criarPedidoService.execute(pedido)).thenReturn(pedido);
when(mapper.toCriarResponse(pedido)).thenReturn(response);
// act
PedidoResponse resultado = controller.criarPedido(request);
// assert
assertThat(resultado).isSameAs(response);
verify(criarPedidoService, times(1)).execute(pedido);
}
}
Teste de mapper (MapStruct)
Mapper simples (interface, sem uses/@Inject) nao precisa de mock — instancie a implementacao gerada via Mappers.getMapper:
import org.mapstruct.factory.Mappers;
class PedidoRestMapperTest {
private final PedidoRestMapper mapper = Mappers.getMapper(PedidoRestMapper.class);
@Test
void deveMapearCampos_quandoConverterRequest() {
CriarPedidoRequest request = new CriarPedidoRequest();
request.setDescricao("Pedido de teste");
Pedido pedido = mapper.toPedido(request);
assertThat(pedido.getDescricao()).isEqualTo("Pedido de teste");
}
}
> Mapper abstract class com @Inject (repository/deps) nao funciona com Mappers.getMapper sem container: instancie a impl gerada (new PedidoMapperImpl()) atribuindo mocks aos campos, ou cubra o mapper via teste do service que o usa.
Armadilha: objetos iguais por Lombok @Data conflitam em stubs
Entidades com @Data geram equals/hashCode de todos campos. Dois objetos criados com new Entidade() (campos nulos) sao iguais pro Mockito. Segundo when(repo.save(obj2)) sobrescreve primeiro when(repo.save(obj1)).
Errado:
Entidade e1 = new Entidade(); // todos os campos nulos
Entidade e2 = new Entidade(); // idem — e1.equals(e2) == true
when(repository.save(e1)).thenReturn(e1);
when(repository.save(e2)).thenThrow(new RuntimeException()); // sobrescreve o anterior!
Correto — usar any() com respostas consecutivas:
Entidade e1 = new Entidade();
Entidade e2 = new Entidade();
when(repository.save(any()))
.thenReturn(e1) // primeira chamada
.thenThrow(new RuntimeException("erro")); // segunda chamada
Armadilha: mensagens de excecao com caracteres acentuados
Fontes projeto usam codificacao que pode gerar caracteres corrompidos (?) nos textos excecao runtime. Evite asserções com acentos em assertThat(ex.getMessage()).contains(...):
Fragil (pode falhar por encoding):
assertThat(ex.getMessage()).contains("número da nota");
assertThat(ex.getMessage()).contains("agrônomo");
assertThat(ex.getMessage()).contains("área tratada");
Robusto — usar substring ASCII que aparece na mensagem:
assertThat(ex.getMessage()).contains("da nota");
assertThat(ex.getMessage()).contains("agr");
assertThat(ex.getMessage()).contains("tratada");
Se mensagem vem de wrapper (ex.: "Erro ao criar o pedido. Motivo: %s"), cheque parte invariante ASCII:
assertThat(ex.getMessage()).contains("Motivo:");
Armadilha: UnnecessaryStubbingException em cenarios de erro
MockitoExtension usa modo estrito. Stub configurado mas nao chamado no teste lanca UnnecessaryStubbingException.
Ocorre tipicamente quando metodo auxiliar (helper mock) adiciona stubs que so seriam invocados no caminho feliz, mas teste cobre caminho erro onde execucao para antes.
Errado — stub de getDataCancelamento() nunca chamado quando gateway lanca excecao:
private Pedido pedidoMock() {
Pedido mock = mock(Pedido.class);
when(mock.getDataCancelamento()).thenReturn(new Timestamp(...)); // stub extra
return mock;
}
@Test
void deveLancarIntegrationException_quandoGatewayFalhar() throws Exception {
Pedido pedido = pedidoMock(); // stub de getDataCancelamento() nao sera usado
when(gateway.cancel(pedido)).thenThrow(new IntegrationException("erro"));
// gateway lanca antes de getDataCancelamento() ser chamado -> UnnecessaryStubbingException
}
Correto — use mock() puro quando caminho nao invoca metodos extras:
@Test
void deveLancarIntegrationException_quandoGatewayFalhar() throws Exception {
Pedido pedido = mock(Pedido.class); // sem stubs desnecessarios
when(gateway.cancel(pedido)).thenThrow(new IntegrationException("erro"));
assertThrows(IntegrationException.class, () -> service.execute(pedido));
}
Boas praticas
- Estrutura AAA: Arrange, Act, Assert.
- Use
@ExtendWith(MockitoExtension.class)pra inicializar mocks. - Prefira
@InjectMocks+ construtor da classe alvo. - Valide comportamento e efeitos observaveis. Evite detalhes internos.
- Um cenario por teste.
- Dado teste simples e explicito.
- Declare
throws Exceptionem metodos teste que interajam com repositorios JapeRepository.
O que evitar
- Teste com dependencia horario real sem controle.
- Assert generico (ex.: so
notNull) quando regra exige validacao forte. - Mockar propria classe sob teste.
- Teste que cobre varios cenarios nao relacionados no mesmo metodo.
- Banco real em teste unitario.
- Mockito 5.x em projetos Java 8 — usar 4.11.0.
- Asserções mensagem com acentos (
ã,ç,ô,úetc.). - Dois
new Entidade()identicos como args de stubs distintos.
Testes de excecao
Quando ha validacao negocio/excecao:
import static org.junit.jupiter.api.Assertions.assertThrows;
@Test
void deveLancarDomainValidationException_quandoEntradaInvalida() throws Exception {
DomainValidationException ex = assertThrows(
DomainValidationException.class,
() -> service.execute(null)
);
assertThat(ex.getMessage()).contains("entrada invalida"); // use substring ASCII
}
Execucao local
Raiz projeto:
./gradlew clean test
Ou no modulo:
./gradlew ::test
Fluxo dev Addon Studio: rode testes antes de deployAddon.
Checklist rapido para PR
- [ ] Existe teste pra nova classe de servico alterada.
- [ ] Cenario sucesso + ao menos um cenario erro relevante cobertos.
- [ ] Sem dependencia externa real em teste unitario.
- [ ]
./gradlew ::testpassando local. - [ ]
throws Exceptiondeclarado em metodos que interagem com repositorios. - [ ] Stubs sem acentos nas asserções de mensagem.
Skills relacionadas
repository— JapeRepository tem quirks — leia antes de mockar (mock via@Mock; estático sóJapeSession/SessionFile)dependency-injection— componentes Guice injetam dependências via construtor — facilita mockcontroller— controllers cobertos por testesentity— fixtures de entidadesmapstruct— mappers cobertos
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: snk-devcenter
- Source: snk-devcenter/addon-studio
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.