Skip to content

borisbrodski/jmockit-xtend

Repository files navigation

JMockit-Xtend

Collection of the extension methods adding JMockit support to the Xtend.

Current release: (more info)

  • JMockit-Xtend: 0.2
    • Xtend: 2.6.0
    • JMockit: 1.10-1.11

Screencast about JMockit-Xtend

If you have 12 minutes to spare, you can watch the new screen cast on the XtextCasts.org:

Content

Introduction

JMockit-Xtend provides a collection of the extension and helper methods to integrate the popular java mocking framework JMockit with the new Xtend language. The goal is not only to provide almost complete original JMockit API to the Xtend but also enhance the API using the power of the Xtend.

The optimal use of the JMockit-Xtend can to achieved together with Jnario.

↑ top

Download

You can download JMockit-Xtend directly from Maven Central repository here or just add it as a maven dependency.

<dependency>
    <groupId>com.github.borisbrodski</groupId>
    <artifactId>jmockit-xtend</artifactId>
    <version>...</version>
</dependency>

WARNING: JMockit needs to be before JUnit on your classpath.

↑ top

Version info

Date Version JMockit versions Xtend versions
09/22/2014 0.2 [1.10,1.11] 2.6.0

↑ top

New cool Xtend-driven API enhancements

Cool things first. Here is a brief overview of the new cool Xtend-powered API

↑ top

Simple mock definitions

JMockit-Xtend provides a set of convenient methods to define the mock configurations:

Example:

stub [
    permissionHelper.allowToSend       // non-strict expectations
    result = true
]

mock [
    permissionHelper.prepareOperation       // strict expectations
    permissionHelper.performOperation       // strict expectations
]

↑ top

any without cast

Thanks to the generic method definition is itn't necessary to cast the any to the expected type. You can do it though to enforce the type, if you want.

Warning: The any method can be used only with the non primitive type. For all primitive types the corresponding anyInt, anyLong, ... methods should be used. The violators will be punished with the NullPointerException.

stub [
    service.acceptString(any, any, any)
    stringBuilder.append(<String>any)    // Force to type to call the right method
    Math::max(any, any)                  // NullPointerException, use anyInt instead
]

For more information see Simple parameter matching with any() and with()

↑ top

Dynamic parameter matching

It's very easy to match the parameter using Xtend-Lambda Expressions:

stub [
    service.acceptString(with [ length > 5 ])
]

↑ top

Dynamic return value

It's also very easy to calculate a return value of the mocked method on the fly:

stub [
    service.generateEMail(any, any, any)
    result = [ String to, String subject, String body |
        '''
            To: «to»
            Subject: «subject»
            «body»
        '''.toString
    ]
]

↑ top

Differences to the original JMockit API

TODO describe:

  • Using with(T) matcher, if at least one matcher used
  • Using withDelegate(Delegate) instead of with(Delegate)

↑ top

Usage

import mockit.Mocked
import org.junit.Test
import static org.junit.Assert.*
import static org.hamcrest.core.Is.*

class MyTest {
    @Test
    def void myTest(MyService service) {
        mock [
            service.doGreeting
            result = "Hello World"
        ]

        assertThat(service.doGreeting, is("Hello World"))
    }

↑ top

Expectations

Expectations using mock []

The mock [] method is an alias to the JMockit new Expectations(){{ ... }} block, which is a strict by default.

mock [
    // Strict expectations
    // - default: times = 1
    // - order is important
    service.call1
    service.call2("x")
    service.call3(1, 2)
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#strictness

↑ top

Non strict expectations using stub []

The stub [] method is an alias to the JMockit new NonStrictExpectations(){{ ... }} block, which is a non-strict by default.

stub [
    // Non-strict expectations
    // - default: times = infinity
    // - order is't important
    service.call1
    service.call2("x")
    service.call3(1, 2)
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#strictness

↑ top

Dynamic partial mocking using mock(class) []

The mock(classOrObject) [] method can be used for the partial mocking.

mock(MyClass) [
    new MyClass().call1
]

You can also grab a mocked instance of the partial mocked class using closure parameter(s):

mock(Api1, Api2) [it, api1, api2 |
    api1.call
    api2.call
]

You may pass up to 5 classes or objects like this: mock(obj1, Class2, obj3, obj4, Class5). If you need to pass more parameters you should use more verbose syntax specifying the closure first:

mock([
    obj1.toString
    result = "obj1"
], obj1, Class2, obj3, obj4, Class5, Class6, obj7, obj8)

mock(class) or stub(class) methods can also be used to initiate a partial mocking:

mock(Class1, Class2)

Expectations about Class1 and Class2 may be specified later on:

mock [
	new Class1().method1
	ins(Class2).method2
]

Defining single partial mock can be combined with creation of an instance of the partial mocked class:

val obj = mock(Class1)

mock [
	obj.method1
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#partial

↑ top

Dynamic partial mocking using stub(classOrObject) []

The stub(classOrObject) [] method can be used for non-strict the partial mocking.

stub(MyClass) [
    service.call1
]

You can also grab a mocked instance of the partial stubbed class using closure parameter(s):

stub(Api1, Api2) [it, api1, api2 |
    api1.call
    api2.call
]

You may pass up to 5 classes or objects like this: stub(obj1, Class2, obj3, obj4, Class5). If you need to pass more parameters you should use more verbose syntax specifying the closure first:

stub([
    obj1.toString
    result = "obj1"
], obj1, Class2, obj3, obj4, Class5, Class6, obj7, obj8)

stub(class) or mock(class) methods can also be used to initiate a partial mocking:

stub(Class1, Class2)

Expectations about Class1 and Class2 may be specified later on:

stub [
	new Class1().method1
	ins(Class2).method2
]

Defining single partial stub can be combined with creation of an instance of the partial mocked class:

val obj = stub(Class1)

stub [
	obj.method1
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#partial

↑ top

Specifying count of iterations for mock/stub expectations

You can specify the count of iterations as a first int parameter of the mock/stub expectation block:

stub(10) [
    service.call1
    returns(1, 2, 3)
]

You can also specify the count of iterations and dynamic partial mocking objects at the same time:

stub(10, Class1, obj2) [
    service.call1
    returns(1, 2, 3)
]

or for that 5 partial mocking parameters:

stub([
    service.call1
    returns(1, 2, 3)
], 10, Class1, obj2, Class3, obj4, obj5, obj6, obj7, Class8)

↑ top

Creating fake instances using ins(class)

In mock[...] and stub[...] expectations blocks you can get a fake instance of any object using ins(class) method. This is particularly useful for partially mocking a class without handy constructor:

stub(ClassWithoutSuitableConstructor) [
	ins(ClassWithoutSuitableConstructor).method
	result = 1
]

Alternatively, you can use closure parameters as described in "Dynamic partial mocking" section:

stub(ClassWithoutSuitableConstructor) [it, instance|
	instance.method
	result = 1
]

Please note: instances created with ins(class) method are not initialized. Calling non-mocked methods on such instances my cause unexpected behavior!

↑ top

Using result=

The result= setter can be used to set one or more results or to throw an exception as a result of the mocked method call.

mock [
    service.methodReturningString
    result = "Hello World!"
    result = new NoMoreGreetingsException
    result = "Ok, the last one"
]

assertThat(service.methodReturningString, is("Hello World!"))
try {
    service.methodReturningString
    fail("Exception expected")
} catch (NoMoreGreetingsException e) {}
assertThat(service.methodReturningString, is("Ok, the last one"))

There are resultInt=, resultLong=, resultShort=, ... methods available to enforce the type casting and reduce the chance of the ClassCastException:

mock [
    service.methodReturningLong
    result = 1L      // With postfix 'L'
    resultLong = 1   // Without postfix 'L'
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#results

↑ top

Xtend-style dynamic result using result= []

The result= setter can be also used to pass a lambda expression instead of a constant result. The lambda expression will be evaluated each time the mocked method is invoked.

mock [
    service.getUniqueId
    result = [| id = id + 1 ]

    service.calculateSum(anyInt, anyInt)
    result = [ int a, int b | a + b ]
]

↑ top

Using returns()

The returns method can be also used to set one or more results but can't be used to throw an exception as a result of the mocked method call. For further information consult the JMockit documentation. (e. g. http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html)

mock [
    service.methodReturningString
    returns("Hello", "World!")
    returns("Ok, the last one")
]

assertThat(service.methodReturningString, is("Hello"))
assertThat(service.methodReturningString, is("World!"))
assertThat(service.methodReturningString, is("Ok, the last one"))

There are also returnsInt=, returnsLong=, returnsShort=, ... methods available to enforce the type casting and reduce the chance of the ClassCastException:

mock [
    service.methodReturningLong
    returns(1L)    // With postfix 'L'
    returns(1)     // Without postfix 'L'
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#results

↑ top

Using onInstance()

The onInstance method can be used to restrict an extected call to a single instance of a mocked class.

mock [
    instance2.callAcceptedOnAllInstances()
    onInstance(instance2).callAcceptedOnlyOnInstance2()
]

Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#onInstance

↑ top

Using times=

The times= (or setTimes()) setter specifies the number of the expected calls to the mocked method.

mock [
    service.sendEmail()
    times = 3
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#constraints

↑ top

Using maxTimes=

The maxTimes= (or setMaxTimes()) setter specifies the maximal number of the expected calls to the mocked method.

mock [
    service.sendEmail()
    maxTimes = 5
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#constraints

↑ top

Using minTimes=

The minTimes= (or setMinTimes()) setter specifies the minimal number of the expected calls to the mocked method.

mock [
	service.sendEmail()
	minTimes = 2
]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#constraints

↑ top

Simple parameter matching with any() and with()

The any(), anyInt(), anyLong(), ... and with(), withInt(), withLong(), ... extension methods are used to statically match the parameter of the mocked method.

WARNING: Unlike JMockit API, you have to wrap the concrete values within with\*() for every invocation, where one of the `any*()' methods are used.

stub [
    service.max(1, 2)               // ok    - no use of any()
    service.max(anyInt, with(3))    // ok    - using with() for the values
    service.max(anyInt, 3)          // ERROR - any() used, but the values without with()

    stringBuilder.append(anyChar)        // Match java.lang.StringBuilder.append(char)
    stringBuilder.append(with(3.3))      // Match java.lang.StringBuilder.append(double)
    stringBuilder.append(withFloat(3.3)) // Match java.lang.StringBuilder.append(float)
]

WARNING: Unlike JMockit API, there are any() extension methods, that has the generic definition:

public static < T > T any(Expectations expectations) throws Exception {
    ...
    return null;
}

As a result, the any() extension method can be used for all kind of parameters without casting. The down side of this method is, that it produces a NullPointerException each time it used with primitive type.

stub [
    service.max(withInt(1), any)            // NullPointerException: Xtend call any.intValue() where any == null
    service.max(withInt(1), anyInt)         // ok: anyInt == (int)0
]

↑ top

Parameter matching with JMockit with*() methods

Following JMockit with\*() methods are supported within "expectations" and "verification" blocks:

  • with(Object)
  • with(T, Object)
  • withAny(T)
  • withEqual(double, double)
  • withEqual(float, double)
  • withEqual(T)
  • withInstanceLike(T)
  • withInstanceOf(Class) Doesn't work with primitive types
  • withMatch(T)
  • withNotEqual(T)
  • withNotNull()
  • withNull()
  • withPrefix(T)
  • withSameInstance(T)
  • withSubstring(T)
  • withSuffix(T)

The JMockit with(Delegate) is renamed to withDelegate(Delegate) allowing the Xtend-lambda expression to be used with the simple with [] syntax

Following JMockit with\*() methods are supported only within the "verifications" block:

  • withCapture()
  • withCapture(Object)

↑ top

Xtend-style dynamic parameter matching using with []

The dynamic parameter matching can be accomplished using Xtend-lambda expression using the with [] extension method for the Object types and withInt [], withLong [], ... extension method for all primitive types.

stub [
    service.processString(with [ length > 5 ])
    service.processString(with [ it?.length > 5 ]) // Prevents NullPointerException if it==null
                                                   // If it==null then (it?.length) evaluates to 0
    service.processInt(withInt [ it > 0 ])

    service.processInt(with [ it > 0 ])  // NullPointerException: withInt should be used
]

↑ top

Mocking private methods

Private method mocking can be accomplished using one of overloaded invoke() extension methods:

stub [
    invoke(object, "methodName", param1, param2)
    result = "result"


]

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#deencapsulation

↑ top

Specifying default results

Other that in the JMockit the default results can be specified within the test class.

class MyTest {
    @Mocked
    MyAPI api

    @Input
    int a = -1 // Return -1 for all methods returning "int"

    @Input
    IOException e // Throw IOException for all methods declaring IOException

    def void myTest() {
        assertThat(api.getInt(), is(-1))
    }
}

JMockit Tutorial: http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#defaultResults

↑ top

TODO

  • README - summarize differences to the JMockit API
  • Iterated expectations
  • Explicit verification
  • Accessing private fields, methods and constructors
  • Cascading mocks
  • Capturing internal instances of mocked types
  • Automatic instantiation and injection of tested classes
  • Reusing expectation and verification blocks
  • State-based testing with JMockit
  • forEachInvocation (see InvocationBlockModifier.java)
  • Add withNull(T) method to allow null parameter matching with invoke()

↑ top

License

JMockit-Xtend is dual-licensed (available under either MIT or EPL licenses):

About

Collection of the Xtend extension methods adding JMockit support

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages