A modern testing and behavioural specification framework for Java 8

Overview

Introduction

If you're a Java developer and you've seen the fluent, modern specification frameworks available in other programming languages such as spock or jasmine then Lambda Behave is for you. Its goal is to make testing a more pleasant experience than it currently is with junit.

The changelog explains what features have been added in each release.

Fluent Specifications

The Lambda Behave Specification design has several goals in mind:

  • To read like plain English.
  • To encourage describing tests using long and descriptive sentences, rather than a few words.
  • An API that is fluent and discoverable nearly entirely through IDE auto-completion.
public class StackSpec {{

    Stack<Integer> stack = new Stack<>();

    describe("a stack", it -> {

        it.isSetupWith(stack::clear);

        it.isConcludedWith(stack::clear);

        it.should("be empty when created", expect -> {
            expect.that(stack).isEmpty();
        });

There are many, many, expectations builtin to the framework - not just isEmpty().

Every specification suite starts its declaration using the Suite.describe method. From that point onwards your IDE should be able to auto-complete the domain specific language for declaring specifications, but just in case you want more information, here's the details.

  • If you want to specify a property about your system use it.should.
  • If you want describe an expectation of that property, use expect.that. This will get you to a fluent API restricted to the type of value that you're making the expectation about. The expectation system is based upon hamcrest. Lambda Behave doesn't compromise the ability to compose matchers in favour of fluency - if you want to compose in more complex flavours simply use expect.that(value).is() and then you can use regular Hamcrest matchers. In my experience this is a rare, albeit useful, breakout option.
  • If you want to setup or teardown data before or after each specification use it.isSetupWith and it.isConcludedWith.
  • If you want to setup or teardown data before or after each suite use it.initializesWith and it.completesWith.
  • Don't worry - I know some Java 8 lambdafied APIs don't deal with exceptions very well but you can throw exceptions in all our callbacks and the appropriate error will be reported, not just break the library.

Data Driven Specifications

The ability to parametrise specifications by different data inputs. Data driven tests in TestNG or the @Parameterized junit annotation perform a similar task. @Parameterized only parameterises at the level of a class, whereas Lambda Behave parameterises at the level of a specification.

describe("a pair of numbers", it -> {
    it.uses(4, 2)
      .and(6, 3)
      .toShow("%d / %d is two", (expect, x, y) -> {
          expect.that(x / y).is(2);
      });
});

The API in Lambda Behave is both fluent and also type safe and doesn't rely on reflection magic. The uses method is overloaded to allow a different number of columns of data to be used. It also supports taking streams or lists of data as its inputs, rather than explicitly chaining individual values.

Not only is the specification parameterised by the data, but the description is also parameterised, its name being interpreted as a format String. The aforementioned test would output the following:

a pair of numbers
  4 / 2 is two
  6 / 3 is two

Generated Specifications

Lambda Behave can automatically generate testcases for your to test your code with, similar to quick check or scala check. The Fluent API for this is similar to data driven specifications allows for control over the way that the values are generated and how many need to be generated. Here is an example of how to show that reversing a String twice returns the same String using randomly generated test case values.

it.requires(10)
  .example(asciiStrings())
  .toShow("reversing a String twice returns the original String", (expect, str) -> {
      String same = new StringBuilder(str).reverse().reverse().toString();
      expect.that(same).isEqualTo(str);
  });

All generated specifications follow this common pattern where;

  • The require clause expresses how many values to generate,
  • The example clause states what type of objects to generate and how to generate them, This is overloaded to allow multiple columns of testcase values to be generated.
  • The toShow clause behaves like a toShow clause for a data drive spec. It is type safe against the the different columns. So in the above example the paramter str will have had its type correctly inferred as String.

Downloading Lambda Behave

If you're using a maven project then you can download Lambda Behave using the following pom entry.

<dependency>
    <groupId>com.insightfullogic</groupId>
    <artifactId>lambda-behave</artifactId>
    <version>0.4</version>
    <scope>test</scope>
</dependency>

If you're using a gradle project then you can use:

testCompile group: 'com.insightfullogic', name: 'lambda-behave', version: '0.3'

There's also an example project and there's published Javadoc.

Junit Integration

Lambda Behave also offers a junit runner. This lets you easily integrate into existing your existing test suite, or the tests via an Eclipse, Intellij, Netbeans, Maven, Gradle or Ant. You just add an annotation to enable this, and it can be run through your normal tooling.

@RunWith(JunitSuiteRunner.class)
public class StackSpec {{

Lambdas - what the hell are they?

Conveniently I've written a book on Lambda expressions in Java 8 and the cleaner code they enable!

Licensing

This library is licensed under the liberal MIT license - see LICENSE for the full details. This means that it's free for any use.

More Details and How to contribute

The wiki has more information.

Comments
  • Maven JAR analyser unable to read target byte code

    Maven JAR analyser unable to read target byte code

    Could just be a Maven plugin incompatibility with Java 8. Feel free to assign to me as a low level bug.

    [WARNING] Unable to process class com/insightfullogic/lambdabehave/BehaveRunner.class in JarAnalyzer File /Users/karianna/Documents/workspace/jclarity/lambda-behave/lambda-behave/target/lambda-behave-0.2-SNAPSHOT.jar org.apache.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 18 at org.apache.bcel.classfile.Constant.readConstant(Constant.java:146) at org.apache.bcel.classfile.ConstantPool.(ConstantPool.java:67) at org.apache.bcel.classfile.ClassParser.readConstantPool(ClassParser.java:222) at org.apache.bcel.classfile.ClassParser.parse(ClassParser.java:136) at org.apache.maven.shared.jar.classes.JarClassesAnalysis.analyze(JarClassesAnalysis.java:92) at org.apache.maven.report.projectinfo.dependencies.Dependencies.getJarDependencyDetails(Dependencies.java:255) at org.apache.maven.report.projectinfo.dependencies.renderer.DependenciesRenderer.hasSealed(DependenciesRenderer.java:1454) at org.apache.maven.report.projectinfo.dependencies.renderer.DependenciesRenderer.renderSectionDependencyFileDetails(DependenciesRenderer.java:536) at org.apache.maven.report.projectinfo.dependencies.renderer.DependenciesRenderer.renderBody(DependenciesRenderer.java:263) at org.apache.maven.reporting.AbstractMavenReportRenderer.render(AbstractMavenReportRenderer.java:79) at org.apache.maven.report.projectinfo.DependenciesReport.executeReport(DependenciesReport.java:186) at org.apache.maven.reporting.AbstractMavenReport.generate(AbstractMavenReport.java:190) at org.apache.maven.plugins.site.ReportDocumentRenderer.renderDocument(ReportDocumentRenderer.java:219) at org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.renderModule(DefaultSiteRenderer.java:319) at org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.render(DefaultSiteRenderer.java:135) at org.apache.maven.plugins.site.SiteMojo.renderLocale(SiteMojo.java:175) at org.apache.maven.plugins.site.SiteMojo.execute(SiteMojo.java:138) at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80) at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51) at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:347) at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:154) at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584) at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:213) at org.apache.maven.cli.MavenCli.main(MavenCli.java:157) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289) at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229) at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415) at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)

    opened by karianna 10
  • lambda behave does not work with recent mockito versions

    lambda behave does not work with recent mockito versions

    lambda behave works great with mockito 2.0.2-beta, but if i upgrade to 2.0.14-beta it fails because mockito uses bytebuddy instead of cglib now and no longer bundles cglib.

    The root problem here is that the bundled cglib in mockito is not public and lamba-behave should not use it.

    opened by christophsturm 7
  • Include class name in JUnit name and child descriptions

    Include class name in JUnit name and child descriptions

    com.insightfullogic.lambdabehave.JunitSuiteRunner doesn't interact too well with the eclipse JUnit runner (and possibly other tools).

    It is not currently possible to jump to a test/spec implementation and running single test cases does not work.

    If the name of the defining spec class is used instead of the suite name then both these behaviours work.

    enhancement 
    opened by hcoles 6
  • Don't hide assertion stack trace when reporting test failures

    Don't hide assertion stack trace when reporting test failures

    In the released 0.4 version, all assertion failures come with a stacktrace that starts at AbstractSuiteRunner:63, since that's where a TestFailure is new-ed up, which is an Exception subclass that is reported as the error.

    By reporing spec.getCause() instead, we get the real line number in the test where the actual assertion is, which is much more valuable.

    opened by jypma 3
  • creates nested junit structure for #76

    creates nested junit structure for #76

    This replaces the existing JunitSuiteRunner with a runner that creates a nested suite for each uniquely named "describe" block in the examined class.

    opened by hcoles 3
  • Non random generation

    Non random generation

    I'd like to be able to use random values quickcheck style during initial development, then fix the seed so that the tests run with deterministic values from that point on.

    I'm not clear if the api supports this, but I couldn't see it documented.

    opened by hcoles 3
  • eclipse junit view - linking back to the source when clicking test name

    eclipse junit view - linking back to the source when clicking test name

    With regular junit , if you double click the test name it takes you to the corresponding source of the test method. but here doing so complains "Test class not found in selected project", so wondered if there was anything take you to the appropriate "should" line in the code somehow

    duplicate 
    opened by nico78 3
  • Add possibility to match message of expected Exception (issue #53)

    Add possibility to match message of expected Exception (issue #53)

    Change witch add possibility to match properties of exception.

    Example:

    expect.exception(Exception.class, () -> {
        // (... some code)
        throw new Exception("expected message");
        // (... some code)
    }).hasProperty("message", is("expected message"));
    
    opened by robertfirek 2
  • Android support via robolectric

    Android support via robolectric

    I tried maintaining a similar library on my own which was a fork of another library Oleaster + robolectric.

    This library looks more maintained and would love to use it on android. Are you open to PR for robolectric?

    opened by bangarharshit 1
  • Added .has syntatic sugar to BoundExpectations

    Added .has syntatic sugar to BoundExpectations

    i like to use FeatureMatcher's to extract data in tests for duplication removal, so given the following:

    expect.that(item).hasProperty("foo", equalTo("bar"));
    

    As soon as this appears twice I will convert it to

    expect.that(item).is(foo(equalTo("bar")));
    

    Where foo is a standard Hamcrest FeatureMatcher

    With lambda-behave this doesn't read as nice and would prefer a has method, so the following would be what I write:

    expect.that(item).has(foo(equalTo("bar")));
    

    This is a simple pass through to is in BoundExpectations

    opened by tddmonkey 1
  • Update setup and teardown methods in readme to reflect 0.3 changes

    Update setup and teardown methods in readme to reflect 0.3 changes

    The setup and teardown methods were renamed in 0.3, however there are still a couple of occurrences of the old methods in the readme.

    This PR fixes this.

    opened by alyphen 1
  • Bump logback-classic from 1.1.2 to 1.2.0

    Bump logback-classic from 1.1.2 to 1.2.0

    Bumps logback-classic from 1.1.2 to 1.2.0.

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump junit from 4.11 to 4.13.1

    Bump junit from 4.11 to 4.13.1

    Bumps junit from 4.11 to 4.13.1.

    Release notes

    Sourced from junit's releases.

    JUnit 4.13.1

    Please refer to the release notes for details.

    JUnit 4.13

    Please refer to the release notes for details.

    JUnit 4.13 RC 2

    Please refer to the release notes for details.

    JUnit 4.13 RC 1

    Please refer to the release notes for details.

    JUnit 4.13 Beta 3

    Please refer to the release notes for details.

    JUnit 4.13 Beta 2

    Please refer to the release notes for details.

    JUnit 4.13 Beta 1

    Please refer to the release notes for details.

    JUnit 4.12

    Please refer to the release notes for details.

    JUnit 4.12 Beta 3

    Please refer to the release notes for details.

    JUnit 4.12 Beta 2

    No release notes provided.

    JUnit 4.12 Beta 1

    No release notes provided.

    Commits
    • 1b683f4 [maven-release-plugin] prepare release r4.13.1
    • ce6ce3a Draft 4.13.1 release notes
    • c29dd82 Change version to 4.13.1-SNAPSHOT
    • 1d17486 Add a link to assertThrows in exception testing
    • 543905d Use separate line for annotation in Javadoc
    • 510e906 Add sub headlines to class Javadoc
    • 610155b Merge pull request from GHSA-269g-pwp5-87pp
    • b6cfd1e Explicitly wrap float parameter for consistency (#1671)
    • a5d205c Fix GitHub link in FAQ (#1672)
    • 3a5c6b4 Deprecated since jdk9 replacing constructor instance of Double and Float (#1660)
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • ArrayExpectation.never() is broken

    ArrayExpectation.never() is broken

    The current implementation of ArrayExpectation.never does nothing, creating a new BoundExpectation object that is immediately discarded and returning this unchanged. Because of this, the expectation conditions are not reversed.

    The proper implementation should presumably be

    return new ArrayExpectation<>(objectUnderTest, !positive);

    opened by Maia-Everett 0
  • Error Prone Static Analysis Tool

    Error Prone Static Analysis Tool

    Looks like you're not using any error-checking in your Java build. This pull requests adds a static analysis tool, Error Prone, created by Google to find common errors in Java code. For example, running mvn compile on the following code:

    public boolean validate(String s) {
    	return s == this.username;
    }
    

    would identify this error:

    [ERROR] src/main/java/HelloWorld.java:[17,17] error: [StringEquality] String comparison using reference equality instead of value equality
    [ERROR]     (see https://errorprone.info/bugpattern/StringEquality)
    

    If you think you might want to try out this plugin, you can just merge this pull request. Please feel free to add any comments below explaining why you did or did not find this recommendation useful.

    opened by cass-green 0
  • Failure tests behaviour

    Failure tests behaviour

    Let's say I have the following test class:

    @RunWith(JunitSuiteRunner.class)
    public class StackSpec {{
    
            Stack<Integer> stack = new Stack<>();
            List<Integer> list = new ArrayList<>();
    
            describe("a stack", it -> {
    
                    it.isSetupWith(stack::clear);
                    it.isConcludedWith(stack::clear);
    
                    // First test executed
                    it.should("be not empty when created", expect -> {
                             stack.add(1);
                            expect.that(stack).isEmpty(); // Will fails
                    });
    
                    // Second test but never executed
                    it.should("be empty when created", expect -> {
                            expect.that(stack).isEmpty();
                    });
    
            }
    
            describe("a list", it -> {
    
                    it.isSetupWith(list::clear);
                    it.isConcludedWith(list::clear);
    
                    // Third test but never executed
                    it.should("be empty when created", expect -> {
                            expect.that(list).isEmpty();
                    });
    
            }
    }}
    

    If one test fails, all others won't be executed from the Test class. Is there a way to prevent this to happen and still continue others tests ?

    opened by martiwi 1
  • Add convenience methods for primitive streams to Description.uses

    Add convenience methods for primitive streams to Description.uses

    Right now, to write an it.uses declaration iterating over a range of ints, I have to write:

    it.uses(IntStream.rangeClosed(low, high).mapToObj(x -> x)).toShow((expect, number) -> ...);
    

    It would be nice if there was a uses declaration accepting IntStream directly (and perhaps LongStream and DoubleStream) so that the mapToObj call could be omitted, reducing clutter.

    opened by Maia-Everett 1
Java testing framework for testing pojo methods

Java testing framework for testing pojo methods. It tests equals, hashCode, toString, getters, setters, constructors and whatever you report in issues ;)

Piotr Joński 48 Aug 23, 2022
Java library for the Siren Hypermedia Type Specification

This is a java library to help with the creation and use of Hypermedia entities as specified by the Siren hypermedia specification. See https://github

Erik Serating 23 May 8, 2022
Layout and functional testing framework for websites

Galen Framework master: Galen is an open-source tool for testing layout and responsive design of web applications. It is also a powerfull functional t

Galen Framework 1.4k Dec 10, 2022
A programmer-oriented testing framework for Java.

JUnit 4 JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks. For more infor

JUnit 8.4k Jan 4, 2023
TestNG testing framework

Documentation available at TestNG's main web site. Release Notes 7.4.0 7.3.0 7.1.0 7.0.0 Need help? Before opening a new issue, did you ask your quest

Cedric Beust 1.8k Jan 5, 2023
JVM version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.

pact-jvm JVM implementation of the consumer driven contract library pact. From the Ruby Pact website: Define a pact between service consumers and prov

Pact Foundation 962 Dec 31, 2022
Advanced Java library for integration testing, mocking, faking, and code coverage

Codebase for JMockit 1.x releases - Documentation - Release notes How to build the project: use JDK 1.8 or newer use Maven 3.6.0 or newer; the followi

The JMockit Testing Toolkit 439 Dec 9, 2022
ScalaTest is a free, open-source testing toolkit for Scala and Java programmers

ScalaTest is a free, open-source testing toolkit for Scala and Java programmers.

ScalaTest 1.1k Dec 26, 2022
Toolkit for testing multi-threaded and asynchronous applications

ConcurrentUnit A simple, zero-dependency toolkit for testing multi-threaded code. Supports Java 1.6+. Introduction ConcurrentUnit was created to help

Jonathan Halterman 406 Dec 30, 2022
Java DSL for easy testing of REST services

Testing and validation of REST services in Java is harder than in dynamic languages such as Ruby and Groovy. REST Assured brings the simplicity of usi

REST Assured 6.2k Dec 31, 2022
Java DSL for easy testing of REST services

Testing and validation of REST services in Java is harder than in dynamic languages such as Ruby and Groovy. REST Assured brings the simplicity of usi

REST Assured 6.2k Dec 25, 2022
Isolated MinIO container management for Java code testing

TestContainers for MinIO MinIO support for the test containers project. Installation Unfortunately, TestContainers for MinIO is not available in any p

Olsi Qose 3 Sep 30, 2022
Cucumber DSL for testing RESTful Web Services

cukes-rest takes simplicity of Cucumber and provides bindings for HTTP specification. As a sugar on top, cukes-rest adds steps for storing and using r

C.T.Co 100 Oct 18, 2022
Randomized Testing (Core JUnit Runner, ANT, Maven)

RANDOMIZED TESTING ================== JUnit test runner and plugins for running JUnit tests with pseudo-randomness. See the following for more infor

null 167 Dec 26, 2022
Captures log entries for unit testing purposes

LogCaptor Install with maven <dependency> <groupId>io.github.hakky54</groupId> <artifactId>logcaptor</artifactId> <version>2.4.0</version>

null 215 Jan 1, 2023
🎉Back end module of Sonic UI automation testing platform. Sonic-UI自动化测试平台后端模块。

?? Sonic UI automation testing platform. English | 简体中文 Background What is sonic ? Nowadays, automation testing, remote control and other technologies

Eason 1.7k Jan 1, 2023
JVM version of Pact Enables consumer driven contract testing

JVM version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.

Pact Foundation 961 Dec 30, 2022
State of the art mutation testing system for the JVM

Pitest (aka PIT) is a state of the art mutation testing system for Java and the JVM. Read all about it at http://pitest.org Releases 1.7.3 #952 Mutate

Henry Coles 1.5k Dec 26, 2022