Pitch: Um framework de testes unitários simples (de verdade) para C, C++ e Objective-C
Fala, pessoal!
Quero compartilhar um projeto que eu desenvolvi porque… sinceramente, testar em C costuma ser mais irritante do que deveria.
O Cest é um framework de testes unitários header-only focado em ser direto ao ponto: sem dependências, sem configuração chata e sem API confusa.
A ideia aqui não é reinventar roda acadêmica — é só tornar testes em C algo minimamente agradável.
🚀 O que o Cest oferece hoje:
- Header-only: só incluir o cest.h e usar
- Zero dependências: nem -lm você precisa
- Multi-linguagem real: C, C++, Objective-C e Objective-C++
- API inspirada em Jest: describe, it, expect
- Type-safe: usando _Generic (C) e overload (C++)
- Output colorido: feedback visual direto no terminal
- Suporte a múltiplos arquivos: estatísticas globais compartilhadas
👀 Exemplo simples:
Exemplo em C
#include "../cest.h"
static int add(int a, int b) { return a + b; }
static double divide(double a, double b) { return b != 0 ? a / b : 0; }
static int nums[] = {1, 2, 3};
static const char* strs[] = {"hello", "world"};
int main() {
describe("C Basic Types", {
it("should handle integers", {
expect(2 + 2).toBe(4);
expect(10 - 3).toEqual(7);
expect(6 * 7).toEqual(42);
});
it("should handle floating point", {
expect(3.14 + 0.86).toBeCloseTo(4.0, 0.001);
expect(divide(10.0, 3.0)).toBeCloseTo(3.333, 0.001);
});
it("should handle booleans", {
expect(true).toBeTruthy();
expect(false).toBeFalsy();
expect(1).toBeTruthy();
expect(0).toBeFalsy();
});
it("should compare strings", {
expect("hello").toEqual("hello");
expect("world").toBe("world");
expect("hello").toContain("ell");
});
it("should handle pointers", {
int x = 42;
expect(&x).toBeTruthy();
expect((void*)NULL).toBeNull();
});
});
describe("C Advanced", {
it("should handle comparisons", {
expect(10).toBeGreaterThan(5);
expect(3).toBeLessThan(7);
});
it("should work with functions", {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toEqual(0);
});
it("should handle NULL pointers", {
int* null_ptr = NULL;
expect(null_ptr).toBeNull();
expect(NULL).toBeFalsy();
});
it("should handle arrays", {
expect(nums).toBeTruthy();
expect(nums[0]).toEqual(1);
expect(nums[1]).toEqual(2);
expect(nums[2]).toEqual(3);
});
it("should handle string arrays", {
expect(strs[0]).toEqual("hello");
expect(strs[1]).toEqual("world");
});
});
return cest_result();
}
Exemplo em Cpp
#include "../cest.h"
#include <vector>
#include <string>
int main() {
describe("C++ Basic Types", {
it("should handle integers", {
expect(2 + 2).toBe(4);
expect(10 - 3).toEqual(7);
});
it("should handle floating point", {
expect(3.14 + 0.86).toBeCloseTo(4.0, 0.001);
});
it("should handle booleans", {
expect(true).toBeTruthy();
expect(false).toBeFalsy();
});
it("should handle std::string", {
std::string s = "hello world";
expect(s).toEqual("hello world");
expect(s).toContain("world");
});
it("should handle pointers", {
int x = 42;
expect(&x).toBeTruthy();
expect(static_cast<void*>(nullptr)).toBeNull();
});
});
describe("C++ STL Containers", {
it("should work with std::vector", {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
expect(v.at(0)).toEqual(1);
expect(v.at(1)).toEqual(2);
expect(v.at(2)).toEqual(3);
});
it("should work with std::string concatenation", {
std::string s1 = "hello";
std::string s2 = "world";
expect(s1 + " " + s2).toEqual("hello world");
});
});
describe("C++ Type Inference", {
it("should infer types correctly", {
expect(3.14).toBeCloseTo(3.14, 0.001);
expect(100).toEqual(100);
expect(true).toBeTruthy();
});
});
return cest_result();
}
Exemplo em ObjC
#import "../cest.h"
static int add_ints(int a, int b) { return a + b; }
int main() {
describe("Objective-C Basic Types", {
it("should handle integers", {
expect(2 + 2).toBe(4);
expect(10 - 3).toEqual(7);
});
it("should handle floating point", {
expect(3.14 + 0.86).toBeCloseTo(4.0, 0.001);
});
it("should handle integers as bool", {
expect(1).toBeTruthy();
expect(0).toBeFalsy();
});
it("should handle pointers", {
int x = 42;
expect(&x).toBeTruthy();
expect((void*)NULL).toBeNull();
});
it("should work with functions", {
expect(add_ints(2, 3)).toBe(5);
expect(add_ints(-1, 1)).toEqual(0);
});
});
describe("Objective-C id Type", {
it("should handle id type", {
id obj1 = (id)0x1234;
id obj2 = (id)0x1234;
expect(obj1).toBe(obj2);
});
it("should handle nil", {
id nil_obj = nil;
expect(nil_obj).toBeNull();
expect(nil_obj).toBeFalsy();
});
it("should work with id pointers", {
id obj = (id)0x5678;
expect(obj).toBeTruthy();
});
});
return cest_result();
}
Exemplo em ObjCpp
#import "../cest.h"
#import <string>
static int add_ints(int a, int b) { return a + b; }
int main() {
describe("Objective-C++ Basic Types", {
it("should handle integers", {
expect(2 + 2).toBe(4);
expect(10 - 3).toEqual(7);
});
it("should handle floating point", {
expect(3.14 + 0.86).toBeCloseTo(4.0, 0.001);
});
it("should handle integers as bool", {
expect(1).toBeTruthy();
expect(0).toBeFalsy();
});
it("should handle std::string", {
std::string s = "hello world";
expect(s).toEqual("hello world");
expect(s).toContain("world");
});
it("should handle pointers", {
int x = 42;
expect(&x).toBeTruthy();
expect(static_cast<void*>(nullptr)).toBeNull();
});
it("should work with functions", {
expect(add_ints(2, 3)).toBe(5);
expect(add_ints(-1, 1)).toEqual(0);
});
});
describe("Objective-C++ id Type", {
it("should handle id type", {
id obj1 = (id)0x1234;
id obj2 = (id)0x1234;
expect(obj1).toBe(obj2);
});
it("should handle nil", {
id nil_obj = nil;
expect(nil_obj).toBeNull();
expect(nil_obj).toBeFalsy();
});
it("should mix ObjC and C++ types", {
id obj = (id)0x5678;
std::string s = "mixed world";
expect(obj).toBeTruthy();
expect(s).toContain("world");
});
});
return cest_result();
}
Sem boilerplate. Sem setup. Só escreve o teste e roda.
🛠️ Decisões técnicas (e possíveis pontos de crítica):
- Uso de _Generic pra simular sobrecarga em C
- Comparação de double com tolerância fixa (sim, dá pra discutir isso)
- Sistema de matchers minimalista (sem macros absurdas ou reflection fake)
- Compatibilidade com Objective-C incluindo id
🎯 Objetivo do projeto:
Ser algo que você adiciona no projeto e usa em 2 minutos — não um framework que exige onboarding.
Se não cumprir isso, falhou no propósito.
Se você já tentou testar algo em C e quase desistiu da vida, quero ouvir sua opinião.
Sem filtro — pode criticar mesmo