Skip to content

Instantly share code, notes, and snippets.

@vsavkin
Last active May 22, 2023 06:03
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save vsavkin/4544346 to your computer and use it in GitHub Desktop.
Save vsavkin/4544346 to your computer and use it in GitHub Desktop.
library test_framework;
import 'package:unittest/unittest.dart';
// RSpec like framework
typedef Closure();
class Example {
String name;
Closure body;
Example(this.name, this.body);
}
class PendingExample extends Example {
PendingExample(name) : super(name, (){});
get name => "${super.name} PENDING";
}
class DeclarationContext {
Context parentContext;
String name;
Map<String, Closure> letDeclarations = {};
List<Example> examples = [];
List<DeclarationContext> children = [];
DeclarationContext(this.name, [this.parentContext = const NullDeclarationContext()]);
DeclarationContext.root() : this("");
DeclarationContext createChild(String name){
var child = new DeclarationContext(name, this);
children.add(child);
return child;
}
declareLet(String name, closure){
letDeclarations[name] = closure;
}
void addExample(String name, Closure body){
if(body === null){
examples.add(new PendingExample(name));
} else {
examples.add(new Example(name, body));
}
}
letDeclaration(String name)
=> letDeclarations.containsKey(name) ? letDeclarations[name] : parentContext.letDeclaration(name);
forEachExample(closure) => examples.forEach(closure);
forEachChildContext(closure) => children.forEach(closure);
}
class NullDeclarationContext {
const NullDeclarationContext();
let(String name) => nil;
}
class ExecutionContext {
DeclarationContext declarationContext;
Map<String, Dynamic> lets = {};
ExecutionContext(this.declarationContext);
bool hasLet(String name) => declarationContext.letDeclaration(name) != null;
let(String name){
lets.putIfAbsent(name, declarationContext.letDeclaration(name));
return lets[name];
}
}
class SpecRunner {
void runSpec(Spec spec){
spec.describe();
_run(spec.declarationContext, spec);
}
_run(declarationContext, spec){
group(declarationContext.name, () {
_runDirectExamples(declarationContext, spec);
_runChildrenExamples(declarationContext, spec);
});
}
_runDirectExamples(declarationContext, spec){
declarationContext.forEachExample((example) {
test(example.name, () {
spec.executionContext = new ExecutionContext(declarationContext);
var res = example.body();
if (res is bool) {
expect(res, isTrue);
}
});
});
}
_runChildrenExamples(declarationContext, spec){
declarationContext.forEachChildContext((c) => _run(c, spec));
}
}
abstract class Spec {
DeclarationContext declarationContext = new DeclarationContext.root();
DeclarationContext executionContext;
void describe();
void let(String name, closure){
declarationContext.declareLet(name, closure);
}
void it(String name, [closure = null]){
declarationContext.addExample(name, closure);
}
void context(String name, closure){
var savedContext = declarationContext;
declarationContext = declarationContext.createChild(name);
closure();
declarationContext = savedContext;
}
noSuchMethod(InvocationMirror invocation){
if(invocation.isGetter){
if(executionContext.hasLet(invocation.memberName)){
return executionContext.let(invocation.memberName);
}
}
throw new NoSuchMethodError(this, invocation.memberName, invocation.positionalArguments, invocation.namedArguments);
}
}
// An example of a spec
class RspecLikeTestingFramework extends Spec {
describe() {
let("firstName", () => "John");
it("works like Rspec", () {
expect(firstName, equals("John"));
});
context("Nested Context", () {
let("lastName", () => "Coltrane");
it("works as well", () {
expect("${firstName} ${lastName}", equals("John Coltrane"));
});
it("this test is just pending");
it("supports one line tests", () => lastName != null);
});
}
}
// How to run specs
// Ideally should use mirrors to find all the subclasses of Spec or all classes ending with Spec.`
main(){
var specRunner = new SpecRunner();
specRunner.runSpec(new RspecLikeTestingFramework());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment