Skip to content

Commit

Permalink
Merge pull request #23 from greghaskins/focused-tests
Browse files Browse the repository at this point in the history
Feature: focused specs with `fit` and `fdescribe`
  • Loading branch information
greghaskins committed Feb 4, 2016
2 parents e5424fe + 632c47a commit 7b69720
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 21 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,21 @@ public class ExampleSpec {{
}}
```

## Supported Features

Spectrum moving toward a `1.0` release with close alignment to Jasmine's API. The library already supports a nice subset of those features:

- [x] `describe`
- [x] `it`
- [x] `beforeEach` / `afterEach`
- [x] `beforeAll` / `afterAll`
- [x] `fit` / `fdescribe`
- [ ] `xit` / `xdescribe`

### Non-Features

Unlike some BDD-style frameworks, Spectrum is _only_ a test runner. Assertions, expectations, mocks, and matchers are the purview of other libraries.

## Getting Started

Spectrum is available as a [package on jCenter](https://bintray.com/greghaskins/maven/Spectrum/view), so make sure you have jCenter declared as a repository in your build config. Future inclusion in Maven Central (see [#12](https://github.com/greghaskins/spectrum/issues/12)) will make this even easier.
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/greghaskins/spectrum/Child.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.greghaskins.spectrum;

import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;

interface Child {

Description getDescription();

void run(RunNotifier notifier);

int testCount();

void focus();

}
14 changes: 14 additions & 0 deletions src/main/java/com/greghaskins/spectrum/Parent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.greghaskins.spectrum;

interface Parent {

void focus(Child child);

static final Parent NONE = new Parent() {

@Override
public void focus(final Child child) {
}
};

}
12 changes: 9 additions & 3 deletions src/main/java/com/greghaskins/spectrum/Spec.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package com.greghaskins.spectrum;

import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;

import com.greghaskins.spectrum.Spectrum.Block;

class Spec extends Runner {
class Spec implements Child {

private final Block block;
private final Description description;
private final Parent parent;


public Spec(final Description description, final Block block) {
public Spec(final Description description, final Block block, final Parent parent) {
this.description = description;
this.block = block;
this.parent = parent;
}

@Override
Expand All @@ -40,4 +41,9 @@ public int testCount() {
return 1;
}

@Override
public void focus() {
this.parent.focus(this);
}

}
34 changes: 33 additions & 1 deletion src/main/java/com/greghaskins/spectrum/Spectrum.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ public static void describe(final String context, final Block block) {
beginDefintion(suite, block);
}

/**
* Focus on this specific suite, while ignoring others.
*
* @param context
* Description of the context for this suite
* @param block
* {@link Block} with one or more calls to {@link #it(String, Block) it} that define each expected behavior
*
* @see #describe(String, Block)
*
*/
public static void fdescribe(final String context, final Block block) {
final Suite suite = getCurrentSuite().addSuite(context);
suite.focus();
beginDefintion(suite, block);
}

/**
* Declare a spec, or test, for an expected behavior of the system in this suite context.
*
Expand All @@ -50,6 +67,21 @@ public static void it(final String behavior, final Block block) {
getCurrentSuite().addSpec(behavior, block);
}

/**
* Focus on this specific spec, while ignoring others.
*
* @param behavior
* Description of the expected behavior
* @param block
* {@link Block} that verifies the system behaves as expected and throws a {@link java.lang.Throwable Throwable}
* if that expectation is not met.
*
* @see #it(String, Block)
*/
public static void fit(final String behavior, final Block block) {
getCurrentSuite().addSpec(behavior, block).focus();
}

/**
* Declare a {@link Block} to be run before each spec in the suite.
*
Expand Down Expand Up @@ -132,7 +164,7 @@ private Value(final T value) {

public Spectrum(final Class<?> testClass) {
final Description description = Description.createSuiteDescription(testClass);
this.rootSuite = new Suite(description);
this.rootSuite = Suite.rootSuite(description);
beginDefintion(this.rootSuite, new ConstructorBlock(testClass));
}

Expand Down
61 changes: 44 additions & 17 deletions src/main/java/com/greghaskins/spectrum/Suite.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,41 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;

import com.greghaskins.spectrum.Spectrum.Block;

class Suite extends Runner {
class Suite implements Parent, Child {

private final CompositeBlock beforeAll = new CompositeBlock();
private final CompositeBlock afterAll = new CompositeBlock();

private final CompositeBlock beforeEach = new CompositeBlock();
private final CompositeBlock afterEach = new CompositeBlock();

private final List<Runner> children = new ArrayList<Runner>();
private final List<Child> children = new ArrayList<Child>();
private final Set<Child> focusedChildren = new HashSet<Child>();

private final Description description;
private final Parent parent;

public Suite(final Description description) {
this.description = description;
public static Suite rootSuite(final Description description) {
return new Suite(description, Parent.NONE);
}

public Suite addSuite(final String name) {
return addSuite(Description.createSuiteDescription(name));
private Suite(final Description description, final Parent parent) {
this.description = description;
this.parent = parent;
}

public Suite addSuite(final Description description) {
final Suite suite = new Suite(description);
public Suite addSuite(final String name) {
final Suite suite = new Suite(Description.createSuiteDescription(name), this);
suite.beforeAll(this.beforeAll);
suite.beforeEach(this.beforeEach);
suite.afterEach(this.afterEach);
Expand All @@ -41,16 +45,19 @@ public Suite addSuite(final Description description) {
}

public Spec addSpec(final String name, final Block block) {
final CompositeBlock specBlockInContext = new CompositeBlock(Arrays.asList(this.beforeAll, this.beforeEach, block, this.afterEach));
final Description specDescription = Description.createTestDescription(this.description.getClassName(), name);
final Spec spec = new Spec(specDescription, specBlockInContext);

final CompositeBlock specBlockInContext = new CompositeBlock(
Arrays.asList(this.beforeAll, this.beforeEach, block, this.afterEach));

final Spec spec = new Spec(specDescription, specBlockInContext, this);
addChild(spec);
return spec;
}

private void addChild(final Runner runner) {
this.description.addChild(runner.getDescription());
this.children.add(runner);
private void addChild(final Child child) {
this.description.addChild(child.getDescription());
this.children.add(child);
}

public void beforeAll(final Block block) {
Expand All @@ -69,6 +76,12 @@ public void afterEach(final Block block) {
this.afterEach.addBlock(block);
}

@Override
public void focus(final Child child) {
this.focusedChildren.add(child);
focus();
}

@Override
public void run(final RunNotifier notifier) {
if (this.testCount() == 0) {
Expand All @@ -81,16 +94,25 @@ public void run(final RunNotifier notifier) {
}

private void runChildren(final RunNotifier notifier) {
for (final Runner child : this.children) {
for (final Child child : this.children) {
runChild(child, notifier);
}
}

private void runChild(final Child child, final RunNotifier notifier) {
if (this.focusedChildren.isEmpty() || this.focusedChildren.contains(child)) {
child.run(notifier);
} else {
notifier.fireTestIgnored(child.getDescription());
}
}

private void runAfterAll(final RunNotifier notifier) {
try {
this.afterAll.run();
} catch (final Throwable e) {
final Description failureDescription = Description.createTestDescription(this.description.getClassName(), "error in afterAll");
final Description failureDescription = Description.createTestDescription(this.description.getClassName(),
"error in afterAll");
this.description.addChild(failureDescription);
notifier.fireTestFailure(new Failure(failureDescription, e));
}
Expand All @@ -104,10 +126,15 @@ public Description getDescription() {
@Override
public int testCount() {
int count = 0;
for (final Runner child : this.children) {
for (final Child child : this.children) {
count += child.testCount();
}
return count;
}

@Override
public void focus() {
this.parent.focus(this);
}

}
Loading

0 comments on commit 7b69720

Please sign in to comment.