How to pass variables between cucumber-jvm steps - java

To pass variables between steps I have the step methods belong to the same class, and use fields of the class for the passed information.
Here is an example as follows:
Feature: Demo
Scenario: Create user
Given User creation form management
When Create user with name "TEST"
Then User is created successfully
Java class with steps definitions:
public class CreateUserSteps {
private String userName;
#Given("^User creation form management$")
public void User_creation_form_management() throws Throwable {
// ...
}
#When("^Create user with name \"([^\"]*)\"$")
public void Create_user_with_name(String userName) throws Throwable {
//...
this.userName = userName;
}
#Then("^User is created successfully$")
public void User_is_created_successfully() throws Throwable {
// Assert if exists an user with name equals to this.userName
}
My question is if it is a good practice to share information between steps? Or would be better to define the feature as:
Then User with name "TEST" is created successfully

In order to share commonalities between steps you need to use a World. In Java it is not as clear as in Ruby.
Quoting the creator of Cucumber.
The purpose of a "World" is twofold:
Isolate state between scenarios.
Share data between step definitions and hooks within a scenario.
How this is implemented is language specific. For example, in ruby,
the implicit self variable inside a step definition points to the
current scenario's World object. This is by default an instance of
Object, but it can be anything you want if you use the World hook.
In Java, you have many (possibly connected) World objects.
The equivalent of the World in Cucumber-Java is all of the objects
with hook or stepdef annotations. In other words, any class with
methods annotated with #Before, #After, #Given and so on will be
instantiated exactly once for each scenario.
This achieves the first goal. To achieve the second goal you have two
approaches:
a) Use a single class for all of your step definitions and hooks
b) Use several classes divided by responsibility [1] and use dependency
injection [2] to connect them to each other.
Option a) quickly breaks down because your step definition code
becomes a mess. That's why people tend to use b).
[1] https://cucumber.io/docs/gherkin/step-organization/
[2] PicoContainer, Spring, Guice, Weld, OpenEJB, Needle
The available Dependency Injection modules are:
cucumber-picocontainer
cucumber-guice
cucumber-openejb
cucumber-spring
cucumber-weld
cucumber-needle
Original post here https://groups.google.com/forum/#!topic/cukes/8ugcVreXP0Y.
Hope this helps.

It's fine to share data between steps defined within a class using an instance variable. If you need to share data between steps in different classes you should look at the DI integrations (PicoContainer is the simplest).
In the example you show, I'd ask whether showing "TEST" in the scenario is necessary at all. The fact that the user is called TEST is an incidental detail and makes the scenario less readable. Why not generate a random name (or hard code something) in Create_user_with_name()?

In Pure java, I just use a Singleton object that gets created once and cleared after tests.
public class TestData_Singleton {
private static TestData_Singleton myself = new TestData_Singleton();
private TestData_Singleton(){ }
public static TestData_Singleton getInstance(){
if(myself == null){
myself = new TestData_Singleton();
}
return myself;
}
public void ClearTestData(){
myself = new TestData_Singleton();
}

I would say that there are reasons to share information between steps, but I don't think that's the case in this scenario. If you propagate the user name via the test steps then it's not really clear from the feature what's going on. I think it's better to specifically say in the scenario what is expected. I would probably do something like this:
Feature: Demo
Scenario: Create user
Given User creation form management
When Create user with name "TEST"
Then A user named "TEST" has been created
Then, your actual test steps might look something like:
#When("^Create user with name \"([^\"]*)\"$")
public void Create_user_with_name(String userName) throws Throwable {
userService.createUser(userName);
}
#Then("^A user named \"([^\"]*)\" has been created$")
public void User_is_created_successfully(String userName) throws Throwable {
assertNotNull(userService.getUser(userName));
}

Here my way: I define a custom Scenario-Scope with spring
every new scenario there will be a fresh context
Feature #Dummy
Scenario: zweites Scenario
When Eins
Then Zwei
1: Use spring
<properties>
<cucumber.version>1.2.5</cucumber.version>
<junit.version>4.12</junit.version>
</properties>
<!-- cucumber section -->
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<!-- end cucumber section -->
<!-- spring-stuff -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>2.4.0.RELEASE</version>
<scope>test</scope>
</dependency>
2: build custom scope class
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope(scopeName="scenario")
public class ScenarioContext {
public Scenario getScenario() {
return scenario;
}
public void setScenario(Scenario scenario) {
this.scenario = scenario;
}
public String shareMe;
}
3: usage in stepdef
#ContextConfiguration(classes = { CucumberConfiguration.class })
public class StepdefsAuskunft {
private static Logger logger = Logger.getLogger(StepdefsAuskunft.class.getName());
#Autowired
private ApplicationContext applicationContext;
// Inject service here : The impl-class need #Primary #Service
// #Autowired
// IAuskunftservice auskunftservice;
public ScenarioContext getScenarioContext() {
return (ScenarioContext) applicationContext.getBean(ScenarioContext.class);
}
#Before
public void before(Scenario scenario) {
ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext) applicationContext).getBeanFactory();
beanFactory.registerScope("scenario", new ScenarioScope());
ScenarioContext context = applicationContext.getBean(ScenarioContext.class);
context.setScenario(scenario);
logger.fine("Context für Scenario " + scenario.getName() + " erzeugt");
}
#After
public void after(Scenario scenario) {
ScenarioContext context = applicationContext.getBean(ScenarioContext.class);
logger.fine("Context für Scenario " + scenario.getName() + " gelöscht");
}
#When("^Eins$")
public void eins() throws Throwable {
System.out.println(getScenarioContext().getScenario().getName());
getScenarioContext().shareMe = "demo"
// you can save servicecall here
}
#Then("^Zwei$")
public void zwei() throws Throwable {
System.out.println(getScenarioContext().getScenario().getName());
System.out.println(getScenarioContext().shareMe);
// you can use last service call here
}
#Configuration
#ComponentScan(basePackages = "i.am.the.greatest.company.cucumber")
public class CucumberConfiguration {
}
the scope class
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class ScenarioScope implements Scope {
private Map<String, Object> objectMap = Collections.synchronizedMap(new HashMap<String, Object>());
/** (non-Javadoc)
* #see org.springframework.beans.factory.config.Scope#get(java.lang.String, org.springframework.beans.factory.ObjectFactory)
*/
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!objectMap.containsKey(name)) {
objectMap.put(name, objectFactory.getObject());
}
return objectMap.get(name);
}
/** (non-Javadoc)
* #see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
*/
public Object remove(String name) {
return objectMap.remove(name);
}
/** (non-Javadoc)
* #see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String, java.lang.Runnable)
*/
public void registerDestructionCallback(String name, Runnable callback) {
// do nothing
}
/** (non-Javadoc)
* #see org.springframework.beans.factory.config.Scope#resolveContextualObject(java.lang.String)
*/
public Object resolveContextualObject(String key) {
return null;
}
/** (non-Javadoc)
* #see org.springframework.beans.factory.config.Scope#getConversationId()
*/
public String getConversationId() {
return "VolatileScope";
}
/**
* vaporize the beans
*/
public void vaporize() {
objectMap.clear();
}
}

Other option is to use ThreadLocal storage. Create a context map and add them to the map. Cucumber JVM runs all the steps in the same thread and you have access to that across all the steps. To make it easier, you can instantiate the storage in before hook and clear in after hook.

If you are using Serenity framework with cucumber you can use current session.
Serenity.getCurrentSession()
more about this feature in http://thucydides-webtests.com/2012/02/22/managing-state-between-steps/. (Serenity was called Thucydides before)

Related

returning all result using spring-data-rest

I am following spring data rest from https://spring.io/guides/gs/accessing-data-rest/ and I am only using
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
I would like to know how can I return all records (without pagination) but not using spring-boot-starter-web.I wants to keep my code as small as possible.
I tried following but it is not working
#RepositoryRestResource(collectionResourceRel = "people" , path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findAllByLastName(#Param("name") String name);
default List<Person> findAll(){
Pageable pageable = null;
return (List<Person>) this.findAll(pageable);
};
}
I mean if I have whole MVC, I can do it but I like to keep my code to minimum.
Spring Data REST is itself a Spring MVC application and is designed in
such a way that it should integrate with your existing Spring MVC
applications with little effort. An existing (or future) layer of
services can run alongside Spring Data REST with only minor additional
work.
If you are using current version of spring boot, there is no need to mark your repository with #RepositoryRestResource; also spring will auto-configure Spring Data Rest once it found the spring-data-rest dependency in your path, bellow you will find steps with minimum config :
In pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
Define your Entity + Repository :
Order.java
#Entity(name = "SampleOrder")
#Data
public class Order {
#Id #GeneratedValue//
private Long id;
private String name;
}
OrderRepository.java
public interface OrderRepository extends CrudRepository<Order, Long> {
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Test your API :
curl http://localhost:8080
< HTTP/1.1 200 OK
< Content-Type: application/hal+json
{ "_links" : {
"orders" : {
"href" : "http://localhost:8080/orders"
}
}
}
As #abdelghani-roussi shows, you can use the CrudRepository instead of the PagingAndSortingRepository, e.g.:
public interface PersonRepository extends CrudRepository<Person, Long> {
List<Person> findAllByLastName(#Param("name") String name);
// don't need to define findAll(), it's defined by CrudRepository
}
and then the default findAll() method will return a List<Person> that isn't paged.
Note: as I mentioned in my comment, by including the dependency on spring-boot-starter-data-rest you are also pulling in the Web dependencies, so you can't avoid that.

PowerMock + Emma - code coverage showing 0% for private static methods and other methods too [duplicate]

This question already has answers here:
PowerMock ECLEmma coverage issue
(9 answers)
Closed 4 years ago.
I have taken a reference of PowerMock from : Mock private method using PowerMockito and applied the same logic here. Also, I installed EMMA (open source tool) in eclipse/STS, but when I run the code I see zero % code coverage. why ?
public class MyClient {
public void publicApi() {
System.out.println("In publicApi");
int result = 0;
try {
result = privateApi("hello", 1);
} catch (Exception e) {
//Assert.fail();
}
System.out.println("result : "+result);
if (result == 20) {
throw new RuntimeException("boom");
}
}
private static int privateApi(String whatever, int num) throws Exception {
System.out.println("In privateAPI");
thirdPartyCall();
int resp = 10;
return resp;
}
private static void thirdPartyCall() throws Exception{
System.out.println("In thirdPartyCall");
//Actual WS call which may be down while running the test cases
}
}
MyClientTest.java
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClient.class)
public class MyClientTest {
#Test
public void testPublicAPI() throws Exception {
PowerMockito.mockStatic(MyClient.class);
//PowerMockito.doReturn(10).when(MyClient.class, "privateApi", anyString(), anyInt());
PowerMockito.when(MyClient.class,"privateApi", anyString(), anyInt()).thenReturn(anyInt());
}
}
Actual Code Coverage:
pom.xml
<dependencies>
<!-- Power Mock -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule-agent</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
If you are constructing a Spy or Mock you are not invoking the actual code under test. The point of spies is to be able to verify() them, in order to check your code behaved correctly by invoking the right callbacks or methods. In the case of mocks the point is to steer code down a particular control flow path and also to verify() expected interactions with the mock.
Since your test case invokes the test method on a spy, it is therefore no wonder your code coverage is exactly 0%. If you were to verify your interactions with the mocked method, you'd probably find that none happened.
What you want to do instead is to setup your mocks but invoke the actual code under test 'the normal way'. The idea is to prime the execution environment, then invoke the tested method call 'normally', and finally observe what actually happened. That last bit consists of normal assertions on the produced output, verification of expected interactions (both that these took place, and also that these involved the expected arguments/values).
Change your test code:
MyClient classUnderTest = PowerMockito.spy(new MyClient());
To:
MyClient classUnderTest = new MyClient();
And watch the code coverage.

Rest service won't work when using CDI: Service stays null

I am making a rest service application with JAX-RS. Its for some project for school. For this project I need to use follow techniques:
• Maven
• JAX-RS
• CDI
• JPA - EJB
• JNDI
• Bean Validation
So now I already maded my domain "Cafes" with a Fake DB ("CafeStub") and a real DB using JPA ("CafeDB"). My domain also makes a little usage of CDI. (#Inject in the CafeService class ...)
Non I wanted to create my rest service, using JAX-RS. This worked fine:
My problem is when I try to use CDI again it fails and it gives an 500 exception, NullPointerException, "Severe: The RuntimeException could not be mapped to a response, re-throwing to the HTTP container"
Full Stacktrace:
I don't know how to fix this, already searched a long time .. Hopefully somebody can help me :s
This is my "CafeController" class. Producing the rest service
Path("/cafes")
public class CafeController {
#Inject
private CafeFacade cafeFacade;
public CafeController() {
//this.cafeFacade = new CafeService();
}
#GET
#Produces("application/json")
public Response getCafes(){
try{
// test ........
ObjectMapper mapper = new ObjectMapper();
Cafe cafe = cafeFacade.getCafe(new Long(1));
String jsonInString = mapper.writeValueAsString(cafe);
return Response.status(200).entity(jsonInString).build();
}catch (JsonProcessingException jsonEx) {
System.out.println("Json Exception");
System.out.println(jsonEx.getMessage());
return null;
}
}
This one is the "CafeService" class, the one who implemented "CafeFacade"
public class CafeService implements CafeFacade {
#Inject
private CafeRepository cafeRepository;
public CafeService() {
//cafeRepository = new CafeStub();
//cafeRepository = new CafeDB("CafesPU");
}
#Override
public long addCafe(Cafe cafe) {
return this.cafeRepository.addCafe(cafe);
}
#Override
public Cafe getCafe(long cafeID) {
return this.cafeRepository.getCafe(cafeID);
}
Her you see the "CafeStub" class, the one who implemented "CafeRepository"
public class CafeStub implements CafeRepository {
private static Map<Long, Cafe> cafes;
private static long counter = 0;
public CafeStub() {
cafes = new HashMap<Long, Cafe>();
// adding some dara
this.addSomeData();
}
#Override
public long addCafe(Cafe cafe) {
if(cafe == null){
throw new DBException("No cafe given");
}
counter++;
cafe.setCafeID(counter);
cafes.put(cafe.getCafeID(), cafe);
return cafe.getCafeID();
}
#Override
public Cafe getCafe(long cafeID) {
if(cafeID < 0){
throw new DBException("No correct cafeID given");
}
if(!cafes.containsKey(cafeID)){
throw new DBException("No cafe was found");
}
return cafes.get(cafeID);
}
At least here you can see my pom.xml (dependencies from CafeService project) - web.xml (from CafeService project) and project structure ...
<dependencies>
<dependency>
<groupId>Cafes</groupId>
<artifactId>Cafes</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.3</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-bundle</artifactId>
<version>1.19.4</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.19.4</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.19.4</version>
</dependency>
</dependencies>
Thanks in advance ...
Cheers
Tom
A class annotated with just #Path does not mark the class as a CDI bean as it is not in the list of bean defining annotations in the CDI spec. Adding RequestScoped to the REST service marks it as a CDI bean so injection works as you've discovered.
This answer here lists the annotations which mark a class as a CDI bean.
Is #javax.annotation.ManagedBean a CDI bean defining annotation?
Solved .. RequestScoped did the trick.. Daimn searched so long for one annotation.
#RequestScoped
#Path("/cafes")
public class CafeController {
Still I don't understand why I need to use it.
#RequestScoped : CDI instantiates and manages the bean
-> I thought my bean.xml would have instantiates and manages the bean ?

Jersey + Jetty + JSON

I would like to produce an JSON using Jetty + Jersey.
My POM.XML is similar to this post: How do I update example to work with latest versions Jetty (9.1.0.RC2) and Jersey (2.7)?. I imagine that i am missing some dependecy. The result from inferFile() is returning blank.
I can see that the method toStirng from Student class was not been called.
Maven
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.1.3.v20140225</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.1.3.v20140225</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.14</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.14</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jetty-http</artifactId>
<version>2.14</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.14</version>
</dependency>
Java
public class Student {
public Student(){
}
#Override
public String toString(){
return new StringBuffer(" First Name : ").append("MY FIRST NAME").toString();
}
}
#Path("/bulkload")
public class BulkLoadAPI {
#POST
#Path("inference")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_JSON)
public Student inferFile() throws URISyntaxException, IOException {
Student s = new Student();
return s;
}
}
public static void main(String[] args) throws Exception {
ServletHolder jerseyServlet = new ServletHolder(ServletContainer.class);
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames", "service.api.BulkLoadAPI");
jerseyServlet.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
Server server = new Server(10500);
ServletContextHandler context = new ServletContextHandler (server, "/", Servl etContextHandler.SESSIONS);
context.addServlet(jerseyServlet, "/*");
server.start();
server.join();
}
I'm not really sure what you're expecting. If you're expecting toString() to be called (which it isn't), that wouldn't even produce valid JSON. The way POJO to JSON (and vice versa) conversion is done is through MessageBodyReaders and MessageBodyWriters. Basically they are looking for fields with either some form of annotation known to the marshaller/unmarshaller, or Java bean style getters and setters. That's how the data/properties for the JSON will be discovered.
For example, if your Student looked like
public class Student {
private String firstName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
it would give you JSON like {"firstName":"MyFirstName"}. You just need to set the property
public Student inferFile() throws URISyntaxException, IOException {
Student s = new Student();
s.setFirstName("MyFirstNaem");
return s;
}
Another thing, this is not needed
setInitParameter("com.sun.jersey.api.json.POJOMappingFeature","true");
That is a Jersey 1 feature. You are using Jersey 2. See also
jersey.config.server.provider.packages
So you don't have to configure each class individually. The value should be a package. It will scan the package and sub packages for annotated classes.

Unit test which verifies only a few elements

There is an interface:
interface IEventListener
{
void onEvent(List <IEvent> events);
}
There is an event class:
class EventB
{
private final int id;
private final A a;
private final String somethingElse;
...
// constructors, getters
...
}
And there is a class to test:
class Doer
{
IEventListener eventListener;
void doSomething(Aaa a)
{
eventListener.onEvent(Arrays.asList(new EventA(1), new EventC(2)));
...
eventListener.onEvent(Arrays.asList(new EventB(42, a.getA(), "foo"), new EventA(3), new EventB(0, a.getA(), "bar")));
...
eventListener.onEvent(Arrays.asList(new EventC(4)));
}
}
The Doer is a code which I need to test, the method doSomething produces packs of events, and I need to test if it produces a particular event in some specific conditions.
More precisely I want to have a unit test which calls the method doSomething and checks that EventB is sent with "42" and A as from method argument a. All other events should be ignored.
To make such test I've only came up with solution involving quite verbose code with ArgumentCaptor, for-blocks, and magic boolean flags...
What is the best way to make a unit test for it? Maybe the code design is bad?
The design is correct, this is how you test it with Mockito:
import org.hamcrest.Matchers;
import org.mockito.Mockito;
public void firesEventsOnDoSomething() {
Listener listener = Mockito.mock(Listener.class);
Doer doer = new Doer(listener);
doer.doSomething(aaa);
Mockito.verify(listener).onEvent(
Mockito.argThat(
Matchers.hasItem(
Matchers.allOf(
Matchers.instanceOf(EventB.class),
Matchers.hasProperty("a", Matchers.equalTo(aaa.getA())),
// whatever you want
)
)
)
);
}
It's Mockito 1.9.0 and Hamcrest-library 1.2.1.
To use JUnit 4.10 together with Hamcrest-library 1.2.1 you should use junit:junit-dep:4.10 artifact, and exclude org.hamcrest:hamcrest-core from it:
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.10</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
If you are using JUnit4 you can try a paremetrized test. Here there is an example http://www.mkyong.com/unittest/junit-4-tutorial-6-parameterized-test/.
If for each of your parameters you have to compare with a different result, you should better consider them as different test cases.
Create dummy implementation of EventListener:
class DummyEventListener implements EventListener {
private int expectedId;
DummyEventListener(int expectedId) {
this.expectedId = expectedId;
}
void onEvent(List <IEvent> events) {
for (IEvent event : events) {
if (!(event instanceof EventB)) {
continue;
}
EventB eb = (EventB)event;
assertEquals(expectedId, eb.getId());
// add more asserts here
}
}
}
Alternatively you can use one of available java mockup frameworks:
EasyMock, JMock, Mockito, JMockit etc.

Categories