Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New "then" method for BDD-style interaction testing #38

Merged
merged 4 commits into from
Mar 18, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 84 additions & 4 deletions src/org/mockito/BDDMockito.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito;

import org.mockito.stubbing.Answer;
import org.mockito.stubbing.OngoingStubbing;
import org.mockito.stubbing.Stubber;
import org.mockito.verification.VerificationMode;

/**
* Behavior Driven Development style of writing tests uses <b>//given //when //then</b> comments as fundamental parts of your test methods.
Expand Down Expand Up @@ -50,6 +51,31 @@
* assertEquals(failure, result);
* </code></pre>
* <p>
* For BDD style mock verification take a look at {@link Then} in action:
* <pre class="code"><code class="java">
* Bike bike = new Bike();
* Person person = mock(Person.class);
*
* public void shouldRideBikeTwice() {
*
* person.ride(bike);
* person.ride(bike);
*
* then(person).should(times(2)).ride(bike);
* }
* </code></pre>
*
* Syntax alternative to the last example:
* <pre class="code"><code class="java">
* public void shouldRideBikeTwice() {
*
* person.ride(bike);
* person.ride(bike);
*
* then(person).verify(times(2)).ride(bike);
* }
* </code></pre>
*
* One of the purposes of BDDMockito is also to show how to tailor the mocking syntax to a different programming style.
*
* @since 1.8.0
Expand Down Expand Up @@ -177,6 +203,60 @@ public <M> M getMock() {
public static <T> BDDMyOngoingStubbing<T> given(T methodCall) {
return new BDDOngoingStubbingImpl<T>(Mockito.when(methodCall));
}

/**
* Bdd style verification of mock behavior.
*
* @see #verify(Object)
* @see #verify(Object, VerificationMode)
*/
public static <T> Then<T> then(T mock) {
return new Then<T>(mock);
}

/**
* Provides fluent way of mock verification.
*
* @author Lovro Pandzic
* @param <T> type of the mock
*/
public final static class Then<T> {

private final T mock;

public Then(T mock) {

this.mock = mock;
}

/**
* @see #verify(Object)
*/
public T should() {
return Mockito.verify(mock);
}

/**
* @see #verify(Object, VerificationMode)
*/
public T should(VerificationMode mode) {
return Mockito.verify(mock, mode);
}

/**
* @see #verify(Object)
*/
public T verify() {
return Mockito.verify(mock);
}

/**
* @see #verify(Object, VerificationMode)
*/
public T verify(VerificationMode mode) {
return Mockito.verify(mock, mode);
}
}

/**
* See original {@link Stubber}
Expand Down
9 changes: 9 additions & 0 deletions src/org/mockito/Mockito.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
* <a href="#26">26. (**New**) Mocking details (Since 1.9.5)</a><br/>
* <a href="#27">27. (**New**) Delegate calls to real instance (Since 1.9.5)</a><br/>
* <a href="#28">28. (**New**) <code>MockMaker</code> API (Since 1.9.5)</a><br/>
* <a href="#29">29. (**New**) BDD style verification (Since 1.9.8)</a><br/>
* </b>
*
* <p>
Expand Down Expand Up @@ -927,6 +928,14 @@
* <p>For more details, motivations and examples please refer to
* the docs for {@link org.mockito.plugins.MockMaker}.
*
*
*
*
* <h3 id="29">29. (**New**) <a class="meaningful_link" href="#BDD_behavior_verification">BDD style verification</a> (Since 1.9.8)</h3>
*
* Enables Behavior Driven Development (BDD) style verification by starting verification with the BDD <b>then</b> keyword.
*
* For more information and an example see {@link BDDMockito}.
*/
@SuppressWarnings("unchecked")
public class Mockito extends Matchers {
Expand Down
121 changes: 101 additions & 20 deletions test/org/mockitousage/customization/BDDMockitoTest.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockitousage.customization;

import org.junit.Test;
import org.mockito.Mock;
import org.mockito.exceptions.misusing.NotAMockException;
import org.mockito.exceptions.verification.WantedButNotInvoked;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockitousage.IMethods;
Expand All @@ -17,17 +19,17 @@
import static org.mockito.BDDMockito.*;

public class BDDMockitoTest extends TestBase {

@Mock IMethods mock;

@Test
public void shouldStub() throws Exception {
given(mock.simpleMethod("foo")).willReturn("bar");

assertEquals("bar", mock.simpleMethod("foo"));
assertEquals(null, mock.simpleMethod("whatever"));
}

@Test
public void shouldStubWithThrowable() throws Exception {
given(mock.simpleMethod("foo")).willThrow(new RuntimeException());
Expand All @@ -47,14 +49,14 @@ public void shouldStubWithThrowableClass() throws Exception {
fail();
} catch(RuntimeException e) {}
}

@Test
public void shouldStubWithAnswer() throws Exception {
given(mock.simpleMethod(anyString())).willAnswer(new Answer<String>() {
public String answer(InvocationOnMock invocation) throws Throwable {
return (String) invocation.getArguments()[0];
}});

assertEquals("foo", mock.simpleMethod("foo"));
}

Expand All @@ -73,7 +75,7 @@ public void shouldStubConsecutively() throws Exception {
given(mock.simpleMethod(anyString()))
.willReturn("foo")
.willReturn("bar");

assertEquals("foo", mock.simpleMethod("whatever"));
assertEquals("bar", mock.simpleMethod("whatever"));
}
Expand All @@ -87,11 +89,11 @@ public void shouldStubConsecutivelyWithCallRealMethod() throws Exception {
assertEquals("foo", mock.simpleMethod());
assertEquals(null, mock.simpleMethod());
}

@Test
public void shouldStubVoid() throws Exception {
willThrow(new RuntimeException()).given(mock).voidMethod();

try {
mock.voidMethod();
fail();
Expand All @@ -113,7 +115,7 @@ public void shouldStubVoidConsecutively() throws Exception {
willDoNothing()
.willThrow(new RuntimeException())
.given(mock).voidMethod();

mock.voidMethod();
try {
mock.voidMethod();
Expand All @@ -133,26 +135,26 @@ public void shouldStubVoidConsecutivelyWithExceptionClass() throws Exception {
fail();
} catch(IllegalArgumentException e) {}
}

@Test
public void shouldStubUsingDoReturnStyle() throws Exception {
willReturn("foo").given(mock).simpleMethod("bar");

assertEquals(null, mock.simpleMethod("boooo"));
assertEquals("foo", mock.simpleMethod("bar"));
}

@Test
public void shouldStubUsingDoAnswerStyle() throws Exception {
willAnswer(new Answer<String>() {
public String answer(InvocationOnMock invocation) throws Throwable {
return (String) invocation.getArguments()[0];
}})
.given(mock).simpleMethod(anyString());

assertEquals("foo", mock.simpleMethod("foo"));
}

class Dog {
public String bark() {
return "woof";
Expand All @@ -168,7 +170,7 @@ public void shouldStubByDelegatingToRealMethod() throws Exception {
//then
assertEquals("woof", dog.bark());
}

@Test
public void shouldStubByDelegatingToRealMethodUsingTypicalStubbingSyntax() throws Exception {
//given
Expand All @@ -187,4 +189,83 @@ public void shouldAllStubbedMockReferenceAccess() throws Exception {

assertEquals(expectedMock, returnedMock);
}

@Test(expected = NotAMockException.class)
public void shouldValidateMockWhenVerifying() {

then("notMock").should();
}

@Test(expected = NotAMockException.class)
public void shouldValidateMockWhenVerifyingWithExpectedNumberOfInvocations() {

then("notMock").should(times(19));
}

@Test(expected = NotAMockException.class)
public void shouldValidateMockWhenVerifyingNoMoreInteractions() {

then("notMock").should();
}

@Test(expected = WantedButNotInvoked.class)
public void shouldFailForExpectedBehaviorThatDidNotHappen() {

then(mock).should().booleanObjectReturningMethod();
}

@Test
public void shouldPassForExpectedBehaviorThatHappened() {

mock.booleanObjectReturningMethod();

then(mock).should().booleanObjectReturningMethod();
}

@Test(expected = WantedButNotInvoked.class)
public void shouldFailForExpectedBehaviorThatDidNotHappenWithVerify() {

then(mock).verify().booleanObjectReturningMethod();
}

@Test
public void shouldPassForExpectedBehaviorThatHappenedWithVerify() {

mock.booleanObjectReturningMethod();

then(mock).verify().booleanObjectReturningMethod();
}

@Test
public void shouldPassFluentBddScenario() {

Bike bike = new Bike();
Person person = mock(Person.class);

person.ride(bike);
person.ride(bike);

then(person).should(times(2)).ride(bike);
}

@Test
public void shouldPassFluentBddScenarioWithVerify() {

Bike bike = new Bike();
Person person = mock(Person.class);

person.ride(bike);
person.ride(bike);

then(person).verify(times(2)).ride(bike);
}

static class Person {

void ride(Bike bike) {}
}

static class Bike {

}
}