Skip to content

Commit eee8dc8

Browse files
Merge pull request #56 from brazzcore/feature/55-flexibility-improvements
[Feature/55] :: flexibility improvements
2 parents 65f9e9a + 3e56b39 commit eee8dc8

34 files changed

+1582
-205
lines changed

README.md

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ The name Mocaí is a tribute to the Brazilian initiative behind the library. It
2121

2222
## 📦 Supported Entities
2323
- Person (with gender, age, CPF)
24+
- Gender (standalone generation)
2425
- Address (street, number, city, state, UF, ZIP)
2526
- Phone (area code, number)
2627
- Company (name, CNPJ)
2728
- CPF (Brazilian individual taxpayer registry)
28-
- CNPJ (Brazilian company registry)
2929
- Certificates (Birth, Marriage, Death)
3030
- National ID (RG)
3131
- Voter Registration (Título de Eleitor)
@@ -64,54 +64,90 @@ import (
6464

6565
func main() {
6666
// Create a Mocker instance for Brazilian Portuguese
67-
mocker := mocai.NewMocker("ptbr", true, nil) // isFormatted: true for formatted docs (e.g., CPF/CNPJ)
67+
mocker := mocai.NewMocker(
68+
mocai.WithLanguage("ptbr"),
69+
mocai.WithFormatted(true),
70+
)
6871

6972
// Generate a mock address
7073
address, err := mocker.NewAddress()
7174
if err != nil {
72-
// Error messages are localized where translation keys are available
7375
log.Fatal(err)
7476
}
7577
fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n", address.Street, address.Number, address.City, address.State, address.UF, address.ZIP)
7678

7779
// Generate a mock person
7880
person, err := mocker.NewPerson()
7981
if err != nil {
80-
// Error messages are localized where translation keys are available
8182
log.Fatal(err)
8283
}
8384
fmt.Printf("Person: %s %s, Gender: %s, Age: %d, CPF: %s\n", person.FirstNameMale, person.LastName, person.Gender.Identity, person.Age, person.CPF.Number)
8485

8586
// Generate a mock company
8687
company, err := mocker.NewCompany()
8788
if err != nil {
88-
// Error messages are localized where translation keys are available
8989
log.Fatal(err)
9090
}
9191
fmt.Printf("Company: %s, CNPJ: %s\n", company.BrazilianCompany.Name, company.BrazilianCompany.CNPJ)
9292
}
93-
9493
```
9594

9695
### Error Messages & Localization
9796

9897
Error messages are localized where translation keys are available. When an error occurs (e.g., invalid data, unsupported language, or generation failure), the error message will be presented in the language configured for the `Mocker` instance, if a translation exists. In some cases, fallback or hardcoded errors may occur if translation coverage is incomplete.
9998

10099
You do not need to perform any extra steps for error localization — Mocai handles this automatically for all supported languages where translation keys are present.
101-
```
102100

103101
> **Note:** Each call to a method like `NewPerson()` or `NewAddress()` generates a new mock with random data. The `Mocker` instance is immutable regarding its configuration (language, formatting, random source).
104102
105103
#### About Languages
106104
Currently, only "ptbr" is implemented. To support other languages, contribute with new translation and mock data files.
107105

108106
#### About Formatting
109-
The `isFormatted` parameter controls whether documents like CPF/CNPJ are returned formatted (e.g., `123.456.789-00`) or as plain numbers (`12345678900`).
107+
The `WithFormatted` option controls whether documents like CPF/CNPJ are returned formatted (e.g., `123.456.789-00`) or as plain numbers (`12345678900`).
108+
109+
### Advanced Usage
110+
111+
#### MockGenerator Interface
112+
The `MockGenerator` interface defines the contract for generating mock data. Use it for dependency injection in your tests:
113+
114+
```go
115+
func CreateUser(generator mocai.MockGenerator) (*User, error) {
116+
person, err := generator.NewPerson()
117+
if err != nil {
118+
return nil, err
119+
}
120+
return &User{Name: person.FirstNameMale + " " + person.LastName}, nil
121+
}
122+
```
123+
124+
#### Custom Providers
125+
You can inject custom providers for Address, Person, and Company to integrate with external APIs or databases:
126+
127+
```go
128+
mocker := mocai.NewMocker(
129+
mocai.WithLanguage("ptbr"),
130+
mocai.WithAddressProvider(myCustomAddressProvider),
131+
mocai.WithPersonProvider(myCustomPersonProvider),
132+
mocai.WithCompanyProvider(myCustomCompanyProvider),
133+
)
134+
```
135+
136+
#### Deterministic Generation
137+
Use `WithRandSource` to provide a custom random source for reproducible test data:
138+
139+
```go
140+
rnd := translations.NewSafeRandSource(myFixedRand)
141+
mocker := mocai.NewMocker(
142+
mocai.WithLanguage("ptbr"),
143+
mocai.WithRandSource(rnd),
144+
)
145+
```
110146

111147
### Examples
112148
The ***examples*** folder contains usage samples:
113149

114-
- `mocker/`: Example using the main entry point `mocai.NewMocker(lang string, isFormatted bool, rnd RandSource)` to generate mocks in a fluent and simplified way.
150+
- `mocker/`: Example using the main entry point `mocai.NewMocker(opts ...Option)` with functional options to generate mocks in a fluent and simplified way.
115151

116152
To run an example:
117153
```sh

docs/localization/pt/README-PT.md

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
![mocai](../../.././img/mocai.svg)
44

5+
#### [README (English)](/README.md)
6+
57
Uma biblioteca Go para geração de dados de teste, permitindo criar mocks de entidades de forma simples e eficiente.
68

79
## 📖 Descrição
@@ -19,11 +21,11 @@ O nome Mocai é uma homenagem à iniciativa brasileira por trás da biblioteca.
1921

2022
## 📦 Entidades Suportadas
2123
- Pessoa (com gênero, idade, CPF)
24+
- Gênero (geração independente)
2225
- Endereço (rua, número, cidade, estado, UF, CEP)
2326
- Telefone (DDD, número)
2427
- Empresa (nome, CNPJ)
2528
- CPF (Cadastro de Pessoa Física)
26-
- CNPJ (Cadastro Nacional de Pessoa Jurídica)
2729
- Certidões (Nascimento, Casamento, Óbito)
2830
- RG (Identidade)
2931
- Título de Eleitor
@@ -62,54 +64,90 @@ import (
6264

6365
func main() {
6466
// Cria uma instância do Mocker para português do Brasil
65-
mocker := mocai.NewMocker("ptbr", true, nil) // isFormatted: true para documentos formatados (ex: CPF/CNPJ)
67+
mocker := mocai.NewMocker(
68+
mocai.WithLanguage("ptbr"),
69+
mocai.WithFormatted(true),
70+
)
6671

6772
// Gera um endereço fictício
6873
address, err := mocker.NewAddress()
6974
if err != nil {
70-
// As mensagens de erro são localizadas quando há chave de tradução disponível
7175
log.Fatal(err)
7276
}
7377
fmt.Printf("Endereço: %s, %d - %s, %s (%s) - %s\n", address.Street, address.Number, address.City, address.State, address.UF, address.ZIP)
7478

7579
// Gera uma pessoa fictícia
7680
person, err := mocker.NewPerson()
7781
if err != nil {
78-
// As mensagens de erro são localizadas quando há chave de tradução disponível
7982
log.Fatal(err)
8083
}
8184
fmt.Printf("Pessoa: %s %s, Gênero: %s, Idade: %d, CPF: %s\n", person.FirstNameMale, person.LastName, person.Gender.Identity, person.Age, person.CPF.Number)
8285

8386
// Gera uma empresa fictícia
8487
company, err := mocker.NewCompany()
8588
if err != nil {
86-
// As mensagens de erro são localizadas quando há chave de tradução disponível
8789
log.Fatal(err)
8890
}
8991
fmt.Printf("Empresa: %s, CNPJ: %s\n", company.BrazilianCompany.Name, company.BrazilianCompany.CNPJ)
9092
}
91-
9293
```
9394

9495
### Mensagens de Erro e Localização
9596

9697
As mensagens de erro são localizadas quando há chave de tradução disponível. Quando ocorre um erro (ex: dados inválidos, idioma não suportado ou falha na geração), a mensagem será apresentada no idioma configurado para a instância do `Mocker`, se houver tradução. Em alguns casos, mensagens fixas podem aparecer caso a cobertura de traduções não seja completa.
9798

9899
Não é necessário realizar nenhuma etapa extra para a localização das mensagens de erro — o Mocai faz isso automaticamente para todos os idiomas suportados onde há chave de tradução.
99-
```
100100

101101
> **Nota:** Cada chamada de método como `NewPerson()` ou `NewAddress()` gera um novo mock com dados aleatórios. A instância do `Mocker` é imutável quanto à configuração (idioma, formatação, fonte de aleatoriedade).
102102
103103
#### Sobre Idiomas
104104
Atualmente, apenas "ptbr" está implementado. Para suportar outros idiomas, contribua com novos arquivos de tradução e mocks.
105105

106106
#### Sobre Formatação
107-
O parâmetro `isFormatted` controla se documentos como CPF/CNPJ são retornados formatados (ex: `123.456.789-00`) ou apenas números (`12345678900`).
107+
A opção `WithFormatted` controla se documentos como CPF/CNPJ são retornados formatados (ex: `123.456.789-00`) ou apenas números (`12345678900`).
108+
109+
### Uso Avançado
110+
111+
#### Interface MockGenerator
112+
A interface `MockGenerator` define o contrato para geração de dados fictícios. Use-a para injeção de dependência nos seus testes:
113+
114+
```go
115+
func CreateUser(generator mocai.MockGenerator) (*User, error) {
116+
person, err := generator.NewPerson()
117+
if err != nil {
118+
return nil, err
119+
}
120+
return &User{Name: person.FirstNameMale + " " + person.LastName}, nil
121+
}
122+
```
123+
124+
#### Providers Customizados
125+
Você pode injetar providers customizados para Address, Person e Company, integrando com APIs externas ou bancos de dados:
126+
127+
```go
128+
mocker := mocai.NewMocker(
129+
mocai.WithLanguage("ptbr"),
130+
mocai.WithAddressProvider(meuProviderDeEndereco),
131+
mocai.WithPersonProvider(meuProviderDePessoa),
132+
mocai.WithCompanyProvider(meuProviderDeEmpresa),
133+
)
134+
```
135+
136+
#### Geração Determinística
137+
Use `WithRandSource` para fornecer uma fonte de aleatoriedade customizada para dados reproduzíveis em testes:
138+
139+
```go
140+
rnd := translations.NewSafeRandSource(meuRandFixo)
141+
mocker := mocai.NewMocker(
142+
mocai.WithLanguage("ptbr"),
143+
mocai.WithRandSource(rnd),
144+
)
145+
```
108146

109147
### Exemplos
110148
O diretório ***examples*** contém exemplos de uso:
111149

112-
- `mocker/`: Exemplo usando o ponto de entrada principal `mocai.NewMocker(lang string, isFormatted bool, rnd RandSource)` para gerar mocks de maneira fluida e simplificada.
150+
- `mocker/`: Exemplo usando o ponto de entrada principal `mocai.NewMocker(opts ...Option)` com functional options para gerar mocks de maneira fluida e simplificada.
113151

114152
Para executar um exemplo:
115153
```sh

examples/mocker/main.go

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,87 @@ package main
22

33
import (
44
"fmt"
5+
"log"
56

67
"github.com/brazzcore/mocai/internal/cli"
78
"github.com/brazzcore/mocai/pkg/mocai"
89
)
910

1011
func main() {
11-
1212
fmt.Println(cli.HeaderMain)
1313
fmt.Println(cli.SubHeader)
1414

15-
m := mocai.NewMocker("ptbr", true, nil)
15+
// Create a Mocker using functional options
16+
m := mocai.NewMocker(
17+
mocai.WithLanguage("ptbr"),
18+
mocai.WithFormatted(true),
19+
)
20+
21+
// Generate a mock person
22+
p, err := m.NewPerson()
23+
if err != nil {
24+
log.Fatal(err)
25+
}
26+
fmt.Printf("Person: %s %s, Gender: %s, Age: %d, CPF: %s\n",
27+
p.FirstNameMale, p.LastName, p.Gender.Identity, p.Age, p.CPF.Number)
28+
29+
// Generate a mock address
30+
addr, err := m.NewAddress()
31+
if err != nil {
32+
log.Fatal(err)
33+
}
34+
fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n",
35+
addr.Street, addr.Number, addr.City, addr.State, addr.UF, addr.ZIP)
36+
37+
// Generate a mock company
38+
comp, err := m.NewCompany()
39+
if err != nil {
40+
log.Fatal(err)
41+
}
42+
fmt.Printf("Company: %s, CNPJ: %s\n", comp.BrazilianCompany.Name, comp.BrazilianCompany.CNPJ)
43+
44+
// Generate a mock CPF
45+
cpfVal, err := m.NewCPF()
46+
if err != nil {
47+
log.Fatal(err)
48+
}
49+
fmt.Printf("CPF: %s\n", cpfVal.Number)
50+
51+
// Generate a mock certificate
52+
cert, err := m.NewCertificate()
53+
if err != nil {
54+
log.Fatal(err)
55+
}
56+
fmt.Printf("Birth Certificate: %s\n", cert.Brazil.BirthCertificate.Number)
57+
58+
// Generate a mock national ID (RG)
59+
nid, err := m.NewNationalID()
60+
if err != nil {
61+
log.Fatal(err)
62+
}
63+
fmt.Printf("RG: %s - %s/%s\n", nid.BrazilianRG.Number, nid.BrazilianRG.IssuingBody, nid.BrazilianRG.State)
64+
65+
// Generate a mock voter registration
66+
vr, err := m.NewVoteRegistration()
67+
if err != nil {
68+
log.Fatal(err)
69+
}
70+
fmt.Printf("Voter Registration: %s (Section: %s, Zone: %s)\n",
71+
vr.BrazilianVoteRegistration.Number, vr.BrazilianVoteRegistration.Section, vr.BrazilianVoteRegistration.Zone)
1672

17-
address, err := m.NewAddress()
73+
// Generate a mock phone
74+
ph, err := m.NewPhone()
1875
if err != nil {
19-
fmt.Println(err)
20-
} else {
21-
fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n",
22-
address.Street, address.Number, address.City, address.State, address.UF, address.ZIP)
76+
log.Fatal(err)
2377
}
78+
fmt.Printf("Phone: (%s) %s\n", ph.AreaCode, ph.Number)
2479

25-
certificate, _ := m.NewCertificate()
26-
fmt.Println("Brazilian birth certificate:", certificate.Brazil.BirthCertificate.Number)
80+
// You can also use the MockGenerator interface for dependency injection:
81+
printLanguage(m)
2782

2883
fmt.Println(cli.Footer)
2984
}
85+
86+
func printLanguage(m mocai.MockGenerator) {
87+
fmt.Printf("\nLanguage: %s\n", m.Language())
88+
}

pkg/mocai/entities/address/generator.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ type Address struct {
1717
ZIP string
1818
}
1919

20-
type SlicesToCheck struct {
20+
// slicesToCheck is an internal helper for validating required data slices.
21+
type slicesToCheck struct {
2122
data []string
2223
err error
2324
}
2425

25-
// NewAddress generates a mock address using a custom language and a random source
26+
// NewAddress generates a mock address using a custom language and a random source.
2627
func NewAddress(lang string, rnd translations.RandSource) (*Address, error) {
2728
addr, err := generateAddress(lang, rnd)
2829
if err != nil {
@@ -37,22 +38,22 @@ func generateAddress(lang string, rnd translations.RandSource) (*Address, error)
3738
supportedLang = "ptbr"
3839
}
3940
if rnd == nil {
40-
rnd = translations.NewSafeRandSource(translations.DefaultRandSource())
41+
rnd = translations.DefaultRandSource()
4142
}
4243

4344
streets := translations.GetList(supportedLang, "address_street")
4445
cities := translations.GetList(supportedLang, "address_city")
4546
states := translations.GetList(supportedLang, "address_state")
4647
zips := translations.GetList(supportedLang, "address_zip")
4748

48-
slicesToCheck := []SlicesToCheck{
49+
checks := []slicesToCheck{
4950
{streets, ErrNoStreets},
5051
{cities, ErrNoCities},
5152
{states, ErrNoStates},
5253
{zips, ErrNoZips},
5354
}
5455

55-
for _, s := range slicesToCheck {
56+
for _, s := range checks {
5657
if len(s.data) == 0 {
5758
return nil, s.err
5859
}

0 commit comments

Comments
 (0)