How to test default value in enum using mockito and testNg - java

I have a third party enum class like this:
package se.api;
public enum InvestigationResultStatus {
PENDING("PendingResult.rr"),
NEW("NewResult.rr"),
MODIFIED("ModifiedResult.rr"),
FINAL("FinalResult.rr"),
PRELIMINARY("PreliminaryResult.rr"),
ADDITIONAL("AdditionalResult.rr"),
REJECTED("RejectedResult.rr"),
COMPLETE("Path.Results.InvestigationInformation.Complete"),
UNDEFINED("");
private final String languageKey;
private InvestigationResultStatus(String languageKey) {
this.languageKey = languageKey;
}
public String getLanguageKey() {
return this.languageKey;
}
}
and in my code I create enum for this class
package se.dto;
public enum InvestigationResultStatus
{
PENDING,
NEW,
MODIFIED,
FINAL,
PRELIMINARY,
ADDITIONAL,
REJECTED,
UNDEFINED
}
to map these api enum to my enum i created enum mapper:
package se.mapper;
import se.dto.InvestigationResultStatus;
public class InvestigationResultStatusMapper
{
public InvestigationResultStatus mapOmResultStatusToNovaResultStatus(final se.api.InvestigationResultStatus investigationResultStatus){
switch (investigationResultStatus){
case PENDING:
return InvestigationResultStatus.PENDING;
case NEW:
return InvestigationResultStatus.NEW;
case MODIFIED:
return InvestigationResultStatus.MODIFIED;
case FINAL:
return InvestigationResultStatus.FINAL;
case PRELIMINARY:
return InvestigationResultStatus.PRELIMINARY;
case ADDITIONAL:
return InvestigationResultStatus.ADDITIONAL;
case REJECTED:
return InvestigationResultStatus.REJECTED;
case UNDEFINED:
return InvestigationResultStatus.UNDEFINED;
default:
throw new IllegalArgumentException("Unknown InvestigationResultStatus: " + investigationResultStatus);
}
}
}
Now I want to test the InvestigationResultStatusMapper class default case path and throw IllegalArgumentException. I search and try it in many ways.But mosta of the examples are in using powermockito.but i want mackito with testNg.How can I test it.I try it using this,
#Test(expectedExceptions = { IllegalArgumentException.class })
public void testMapOmResultStatusToNovaResultStatus_whenHaveDefaultValue_thenThrowIllegalArgumentException()
{
se.api.InvestigationResultStatus investigationResultStatusNewEnum = mock(se.api.InvestigationResultStatus.class);
Whitebox.setInternalState(investigationResultStatusNewEnum, "name", "DEFAULT");
Whitebox.setInternalState(investigationResultStatusNewEnum, "ordinal", "DefaultResult.rr");
se.api.InvestigationResultStatus investigationResultStatusCosmic = mock(se.api.InvestigationResultStatus.class);
when(investigationResultStatusCosmic.values()).thenReturn(
new se.api.InvestigationResultStatus[] { InvestigationResultStatus.PENDING, InvestigationResultStatus.NEW,
se.api.InvestigationResultStatus.MODIFIED, InvestigationResultStatus.FINAL, InvestigationResultStatus.PRELIMINARY,
se.api.InvestigationResultStatus.ADDITIONAL, InvestigationResultStatus.REJECTED,
se.api.InvestigationResultStatus.COMPLETE, InvestigationResultStatus.UNDEFINED, investigationResultStatusNewEnum });
investigationResultStatusMapper.mapOmResultStatusToNovaResultStatus(investigationResultStatusNewEnum);
}

you can create directly an instance in order to avoid mocks.
#Test(expectedExceptions = { IllegalArgumentException.class })
public void testMapOmResultStatusToNovaResultStatus_whenHaveDefaultValue_thenThrowIllegalArgumentException()
{
InvestigationResultStatus investigation = new InvestigationResultStatus();
investigation.mapOmResultStatusToNovaResultStatus(-10000);
....
}

In this I'm unabale to mock enum(final) class using mockito
#BeforeClass
public void setUp()
{
investigationResultStatusMapper = new InvestigationResultStatusMapper();
investigationResultStatusNewEnum = PowerMockito.mock(InvestigationResultStatus.class);
Whitebox.setInternalState(investigationResultStatusNewEnum, "name", "TEST");
Whitebox.setInternalState(investigationResultStatusNewEnum, "ordinal", 9);
InvestigationResultStatus[] investigationResultStatuses = Arrays
.copyOf(InvestigationResultStatus.values(), InvestigationResultStatus.values().length + 1);
investigationResultStatuses[investigationResultStatuses.length - 1] = investigationResultStatusNewEnum;
PowerMockito.mockStatic(InvestigationResultStatus.class);
PowerMockito.when(InvestigationResultStatus.values()).thenReturn(
investigationResultStatuses);
}
/**
* Test Method for {#link InvestigationResultStatusMapper#mapOmResultStatusToNovaResultStatus(InvestigationResultStatus)}
* extend the InvestigationResultStatus enum for DEFAULT throw {#link IllegalArgumentException})
*/
#Test(expectedExceptions = {
IllegalArgumentException.class }, expectedExceptionsMessageRegExp = "Unknown InvestigationResultStatus: TEST")
public void testMapOmResultStatusToNovaResultStatus_whenHaveDefaultValue_thenThrowIllegalArgumentException()
{
investigationResultStatusMapper.mapOmResultStatusToNovaResultStatus(investigationResultStatusNewEnum);
}

Wrong approach. First of all, the 3rd party enum has 9 entries, but your own enum has only 8. So when your mapping code receives COMPLETE it will throw that exception! But if your own enum would have 9 entries, and you just map them 1-1 ... what is the point of using your own enum here in the first place? (there might be good reasons to have it, but at this point, it doesn't add much value).
Beyond that problem, you should do this completely different:
You put these mappings into a Map object. Then write a unit tests that uses InvestigationResultStatus.values() to acquire an array with all valid InvestigationResultStatus constants. You make sure that your map knows how to map all these valid constants (for example by checking that you get a non-null return value). Additionally, you might have one test for each 3rd party constant to check for the expected result.
The key point here is: unfortunately, enums might change over time. So you absolutely want to know when additional constants are added to the 3rd enum.
In other words: the java language respectively the compiler makes sure that there can't be "invalid" instances of that 3rd party enum. When you have InvestigationResultStatus someEnumValue, then it is 100% clear that: either someValue is null, or it is one of the constants from that enum. Nothing else is possible here! Thus: no need to expect, check or handle "invalid" values.

Related

Which design pattern to use to avoid if/else in validation classes?

I am currently using HibernateConstraintValidator to implement my validations. But my reviewer is not fine with having if/else in code or ! operators. Which design pattern can I use to remove the if/else in my validation logic?
public class SomeValidatorX implements ConstraintValidator<SomeAnnotation, UUID> {
#Autowired
SomeRepository someRepository;
#Override
public boolean isValid(UUID uuid, ConstraintValidationContext context) {
return !(uuid!=null && someRepository.existsById(uuid)); //The reviewer doesn't want this negation operator
}
}
And in below code, he doesn't want if/else
public class SomeValidatorY implements ConstraintValidator<SomeAnnotation, SomeClass> {
#Autowired
SomeRepository someRepository;
#Override
public boolean isValid(SomeClass someObject, ConstraintValidationContext context) {
if(someObject.getFieldA() != null) { //He doesn't want this if statement
//do some operations
List<Something> someList = someRepository.findByAAndB(someObject.getFieldA(),B);
return !someList.isEmpty(); //He doesn't want this ! operator
}
return false; // He was not fine with else statement in here as well
}
}
Side Note: We have to use Domain Driven Design (if it helps)
A long time ago, in the beginning of time. There was a guideline that said that methods should only have one exit point. To achieve that, developers had to track the local state and use if/else to be able to reach the end of the method.
Today we know better. By exiting a method as early as possible it's much easier to keep the entire flow in our head while reading the code. Easier code means less mistakes. Less mistakes equals less bugs.
In my opinion, that's why the reviewer doesn't like the code. It's not as easy to read as it could be.
Let's take the first example:
public boolean isValid(UUID uuid, ConstraintValidationContext context) {
return !(uuid!=null && someRepository.existsById(uuid)); //The reviewer doesn't want this negation operator
}
What the code says is "not this: (uuid should not be empty and it must exist)". Is that easy to understand? I think not.
The alternative: "Its OK if uuid do not exist, but if it do, the item may not exist".
Or in code:
if (uuid == null) return true;
return !someRepository.existsById(uuid);
Much easier to read, right? (I hope that I got the intention correct ;))
Second example
if(someObject.getFieldA() != null) { //He doesn't want this if statement
//do some operations
List<Something> someList = someRepository.findByAAndB(someObject.getFieldA(),B);
return !someList.isEmpty(); //He doesn't want this ! operator
}
return false; // He was not fine with else statement in here as well
Ok. Here you are saying:
If field A is not null:
Build a list where A and b is found
If that list is not empty fail, otherwise succeed.
Otherwise fail
A easier way to conclude that is to simply say:
It's ok if field A is not specified
If field A is specified it must exist in combination with B.
Translated to code:
if (someObject.getFieldA() == null)
return true;
return !someRepository.findByAAndB(someObject.getFieldA(),B).isEmpty();
In C# we have Any() which is opposite to isEmpty which I would prefer in this case as it removes the negation.
Sometimes negations are required. It doesn't make sense to write a new method in the repository to avoid it. However, if findByAAndB is only used by this I would rename it to ensureCombination(a,b) so that it can return true for the valid case.
Try to write code as you talk, it makes it much easier to create a mental picture of the code then. You aren't saying "Im not full, lets go to lunch", are you? ;)
You can check the Null-object pattern.
The general pattern is to ban null completely from your code. This eliminates the ugly null checks. In this point I agree with your code reviewer.
Following the below recommendations will result in:
public boolean isValid(SomeClass someObject, ConstraintValidationContext context) {
return someRepository.containsAAndB(someObject.getFieldA(), B);
}
Avoid null checks
Before introducing the Null-object pattern, simply apply the pattern or convention to enforce initialization of all references. This way you can be sure that there are no null references in your entire code.
So when you encounter a NullPointerException, you don't solve the issue by introducing a null check, but by initializing the reference (on construction) e.g., by using default values, empty collections or null objects.
Most modern languages support code analysis via annotations like #NonNull that checks references like arguments and will throw an exception, when a parameter is null/not initialized. javax.annotation for instance provides such annotations.
public void operation(#NonNull Object param) {
param.toString(); // Guaranteed to be not null
}
Using such annotations can guard library code against null arguments.
Null-Object Pattern
Instead of having null references, you initialize each reference with a meaningful value or a dedicated null-object:
Define the Null-object contract (not required):
interface NullObject {
public boolean getIsNull();
}
Define a base type:
abstract class Account {
private double value;
private List<Owner> owners;
// Getters/setters
}
Define the Null-object:
class NullAccount extends Account implements NullObject {
// Initialize ALL attributes with meaningful and *neutral* values
public NullAccount() {
setValue(0); //
setOwners(new ArrayList<Owner>())
#Override
public boolean getIsNull() {
return true;
}
}
Define the default implementation:
class AccountImpl extends Account implements NullObject {
#Override
public boolean getIsNull() {
return true;
}
}
Initialize all Account references using the NullAccount class:
class Employee {
private Account Account;
public Employee() {
setAccount(new NullAccount());
}
}
Or use the NullAccount to return a failed state instance (or default) instead of returning null:
public Account findAccountOf(Owner owner) {
if (notFound) {
return new NullAccount();
}
}
public void testNullAccount() {
Account result = findAccountOf(null); // Returns a NullAccount
// The Null-object is neutral. We can use it without null checking.
// result.getOwners() always returns
// an empty collection (NullAccount) => no iteration => neutral behavior
for (Owner owner : result.getOwners()) {
double total += result.getvalue(); // No side effect.
}
}
Try-Do Pattern
Another pattern you can use is the Try-Do pattern. Instead of testing the result of an operation you simply test the operation itself. The operation is responsible to return whether the operation was successful or not.
When searching a text for a string, it might be more convenient to return a boolean whether the result was found instead of returning an empty string or even worse null:
public boolean tryFindInText(String source, String searchKey, SearchResult result) {
int matchIndex = source.indexOf(searchKey);
result.setMatchIndex(matchIndex);
return matchIndex > 0;
}
public void useTryDo() {
SearchResult result = new Searchresult();
if (tryFindInText("Example text", "ample", result) {
int index = result.getMatchIndex();
}
}
In your special case, you can replace the findByAAndB() with an containsAAndB() : boolean implementation.
Combining the patterns
The final solution implements the Null-Object pattern and refactors the find method. The result of the original findByAAndB() was discarded before, since you wanted to test the existence of A and B. A alternative method public boolean contains() will improve your code.
The refactored implementation looks as followed:
abstract class FieldA {
}
class NullFieldA {
}
class FieldAImpl {
}
class SomeClass {
public SomeClass() {
setFieldA(new NullFieldA());
}
}
The improved validation:
public boolean isValid(SomeClass someObject, ConstraintValidationContext context) {
return someRepository.containsAAndB(someObject.getFieldA(), B);
}
You can try this
return Optional.ofNullable(uuid)
.map(someRepository::existsById)
.orElse(false);

Instantiate class dynamically based on some constant in Java

I am making a multiplayer game which makes heavy use of a serialisable Event class to send messages over a network. I want to be able to reconstruct the appropriate subclass of Event based on a constant.
So far I have opted for the following solution:
public class EventFactory {
public static Event getEvent(int eventId, ByteBuffer buf) {
switch (eventId){
case Event.ID_A:
return EventA.deserialise(buf);
case Event.ID_B:
return EventB.deserialise(buf);
case Event.ID_C:
return EventC.deserialise(buf);
default:
// Unknown Event ID
return null;
}
}
}
However, this strikes me as being very verbose and involves adding a new 'case' statement every time I create a new Event type.
I am aware of 2 other ways of accomplishing this, but neither seems better*:
Create a mapping of constants -> Event subclasses, and use clazz.newInstance() to instantiate them (using an empty constructor), followed by clazz.initialiase(buf) to supply the necessary parameters.
Create a mapping of constants -> Event subclasses, and use reflection to find and call the right method in the appropriate class.
Is there a better approach than the one I am using? Am I perhaps unwise to disregard the alternatives mentioned above?
*NOTE: in this case better means simpler / cleaner but without compromising too much on speed.
You can just use a HashMap<Integer,Event> to get the correct Event for the eventID. Adding or removing events is going to be easy, and as the code grows this is easy to maintain when compared to switch case solution and speed wise also this should be faster than switch case solution.
static
{
HashMap<Integer,Event> eventHandlerMap = new HashMap<>();
eventHandlerMap.put(eventId_A, new EventHandlerA());
eventHandlerMap.put(eventId_B, new EventHandlerB());
............
}
Instead of your switch statement Now you can just use :
Event event = eventHandlerMap.get(eventId);
if(event!=null){
event.deserialise(buf);
}
If you're not afraid of reflection, you could use:
private static final Map<Integer, Method> EVENTID_METHOD_MAP = new LinkedHashMap<>();
static {
try {
for (Field field : Event.class.getFields())
if (field.getName().startsWith("ID_")) {
String classSuffix = field.getName().substring(3);
Class<?> cls = Class.forName("Event" + classSuffix);
Method method = cls.getMethod("deserialize", ByteBuffer.class);
EVENTID_METHOD_MAP.put(field.getInt(null), method);
}
} catch (IllegalAccessException|ClassNotFoundException|NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Event getEvent(int eventId, ByteBuffer buf)
throws InvocationTargetException, IllegalAccessException {
return (Event) EVENTID_METHOD_MAP.get(eventId).invoke(null, buf);
}
This solution requires that int ID_N always maps to class EventN, where N can be any String where all characters return true for the method java.lang.Character.isJavaIdentifierPart(c). Also, class EventN must define a static method called deserialize with one ByteBuffer argument that returns an Event.
You could also check if field is static before trying to get its field value. I just forget how to do that at the moment.

Php, clone objects, add new methods, or overwrite them

For testing purpose, I try to "fake" some objects. I want to do the following: I have an object, and want to add new methods, or overwrite some. Sadly, unlike in Java, its not possible to create "nameless" classes. Ok, I could do it by simply creating a new class, but I want to do it dinamicaly.
This is the class:
class Test
{
public function method1()
{
return 'oldmethod1';
}
public function method2()
{
return 'oldmethod2';
}
public static function staticmethod1()
{
return 'staticmethod1';
}
public static function staticmethod2()
{
return 'staticmethod2';
}
}
and now what I want to do:
$a = new Test();
$b = new CreateMockObjectFromObject($a);
$b->newmethod = function() { return 'newmethod'; };
$b->method2 = function() { return 'method2 is overwritten'; };
$b->staticmethod2 = function() { return 'staticmethod2 overwritten'; };
echo $b->method1().'<br>';
echo $b->method2().'<br>';
echo $b::staticmethod1().'<br>';
echo $b::staticmethod2().'<br>';
Here you can see my wishes: call a normal method, overwrite a method, call a static method, overwrite a method. The results: FAIL, SUCCESS, FAIL, FAIL.
I have a helper class:
class CreateMockObjectFromObject
{
private $sourceObj;
/**
* #return
*/
public function __call ($method, $args)
{
if (isset($this->sourceObj->$method))
{
return call_user_func_array($this->sourceObj->$method, $args);
}
if (isset($this->$method))
{
return call_user_func_array($this->$method, $args);
}
throw new Exception ($method.' NOT FOUND');
}
/**
* #return
*/
public static function __callStatic ($method, $args)
{
// I cant even imagine this...
}
/**
* #return CreateMockObjectFromObject
*/
public function __construct ($sourceObj)
{
$this->sourceObj = $sourceObj;
}
}
I cant even imagine what about static methods. How to write this helper class so that all mocking/faking can work? And I didnt even talk about "const"-s...
once again, I know it all could be done with extending, but I need to do in this way!
Mocking an entire class:
$mock = Mockery::mock('FQ\ClassName');
Mocking only certain methods:
$mock = Mockery::mock('FQ\ClassName[method1, method2]')
Here, method3, method4, ..., methodN will function exactly as they do in your class implementation.
Making an expectation:
$mock->shouldReceive('method1')
->once()
->andReturn('a value')
;
After you make an expectation, then you Act upon the system under test:
$return = $objectImTesting->performAction($mock);
Once you act, you should assert that any return value is what it should be, given the input you provided:
$this->assertEquals('a value', $return);
Taking the above example, the test will fail if any of the following conditions are true:
method1 on your mock object is never called
method1 on your mock object is called more than once
the return value of performAction is not the literal value 'a value'
Now, you've not reinvented the wheel and instead already gotten started writing tests. Also for free, you get everything that PHPUnit and Mockery provide you (how long do you think it would take you by yourself to be able to support Demeter Chains in your mocking framework?).
Don't get me wrong, by all means go ahead and develop your testing framework if all you're interested in is learning. However, in my opinion I would not want to trust a testing framework that I developed by myself to ensure the code that I write works. I'd much rather use something that's open source and has been around for awhile so that I can sleep easier at night.
More information:
PHPUnit Documentation
Mockery Documentation

Unique enum member values

My problem: I want to specify a 1 to 1 connection between two enums. basically:
enum MyEnum{
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3,
}
enum MyOtherEnum{
OTHER_ENUM_VALUE_1(MyEnum.ENUM_VALUE_3),
OTHER_ENUM_VALUE_2(MyEnum.ENUM_VALUE_1),
OTHER_ENUM_VALUE_3,
OTHER_ENUM_VALUE_4(MyEnum.ENUM_VALUE_2),
private MyEnum pair;
MyOtherEnum(MyEnum pair){
this.pair=pair;
}
}
Given this code, what is the best way to make sure, that two instances of MyOtherEnum doesn't have the same MyEnum pair? I figured some solutions, but neither seem to be good. One solution is throw an exception in the constructor, but I'm not sure if that is even doable. Another solution is a special getter method, but it can be easily bypassed with simple MyOtherEnum.values() calls.
It sounds like you shouldn't implement this as a runtime check. It can only fail if a developer who has control over the code did some bad changes. Detecting changes that introduce bugs is what tests are for.
Write a simple test that checks if everything is correct. The other answers already provide some good solutions to check for duplicated enums. Instead of running the application to see if the enum initialisation fails, run the test (I hope you already write and run tests anyway).
I put the "why" aside and try to answer your question:
Create a static block in MyOtherEnum that performs the check:
static{
// This set stores all the used values
EnumSet<MyEnum> usedValues = EnumSet.noneOf(MyEnum.class);
for(MyOtherEnum e : values()){
// Value already present in the set?
if(usedValues.contains(e.pair)) throw ...; // Duplicate
usedValues.add(e);
}
}
But I have to agree with the comments: Instead of writing this check, you can also simply look at your code :).
How about this:
public enum MyEnum {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3,
}
// All enums taken.
private static final Set<MyEnum> taken = EnumSet.noneOf(MyEnum.class);
enum MyOtherEnum {
OTHER_ENUM_VALUE_1(MyEnum.ENUM_VALUE_3),
OTHER_ENUM_VALUE_2(MyEnum.ENUM_VALUE_1),
OTHER_ENUM_VALUE_3(MyEnum.ENUM_VALUE_2),
OTHER_ENUM_VALUE_4(MyEnum.ENUM_VALUE_2);
private MyEnum pair;
MyOtherEnum(MyEnum pair) {
if (taken.contains(pair)) {
throw new IllegalArgumentException("Cannot pair the same MyEnum with two other enums! " + this);
}
taken.add(pair);
this.pair = pair;
}
}
public void test() {
System.out.println("Hello: " + MyOtherEnum.OTHER_ENUM_VALUE_4);
}
Im a bit confused, by your question. Have you considered using HashSet?
A HashSet can be used to ensure unique values, then simply add the values used in the HashSet if it returns false the value already exists.
You can use a static block to test your linkage:
import java.util.HashSet;
import java.util.Set;
public class EnumTrick {
enum MyEnum {
ENUM_VALUE_1, ENUM_VALUE_2, ENUM_VALUE_3,
}
enum MyOtherEnum {
OTHER_ENUM_VALUE_1(MyEnum.ENUM_VALUE_3),
OTHER_ENUM_VALUE_2(MyEnum.ENUM_VALUE_1),
OTHER_ENUM_VALUE_3(MyEnum.ENUM_VALUE_2),
OTHER_ENUM_VALUE_4(MyEnum.ENUM_VALUE_2);
private MyEnum pair;
MyOtherEnum(MyEnum pair) {
this.pair = pair;
}
static {
Set<MyEnum> usedEnums = new HashSet<MyEnum>();
for(MyOtherEnum moe : MyOtherEnum.values()){
if(! usedEnums.add(moe.pair) ){
throw new IllegalArgumentException(moe.pair + " is already used!");
}
}
}
}
public static void main(String [] args){
MyOtherEnum moe = MyOtherEnum.OTHER_ENUM_VALUE_1;
}
}
Will throw an exception:
Exception in thread "main" java.lang.ExceptionInInitializerError
at EnumTrick.main(EnumTrick.java:35)
Caused by: java.lang.IllegalArgumentException: ENUM_VALUE_2 is already used!
at EnumTrick$MyOtherEnum.<clinit>(EnumTrick.java:28)
... 1 more

Easymock: does the order of captures matter?

This might seem like a pretty detailed question about Easymock, but I'm having a hard time finding a support site/forum/mailing list for this library.
I'm encountering a bug when using the captures() method that seems to return the captured parameters out of order.
Here's a simplified version of what I am testing:
public class CaptureTest extends TestCase {
// interface we will be mocking
interface Processor {
void process(String x);
}
// class that uses the interface above which will receive the mock
class Component {
private Processor processor;
private String[] s = { "one", "two", "three", "four" };
Component(Processor processor) {
this.processor = processor;
}
public void doSomething() {
for (int i = 0; i < s.length; i++) {
processor.process(s[i]);
}
}
}
public void testCapture() {
//create the mock, wire it up
Processor mockProcessor = createMock(Processor.class);
Component component = new Component(mockProcessor);
//we're going to call the process method four times
//with different arguments, and we want to capture
//the value passed to the mock so we can assert against it later
Capture<String> cap1 = new Capture<String>();
Capture<String> cap2 = new Capture<String>();
Capture<String> cap3 = new Capture<String>();
Capture<String> cap4 = new Capture<String>();
mockProcessor.process(and(isA(String.class), capture(cap1)));
mockProcessor.process(and(isA(String.class), capture(cap2)));
mockProcessor.process(and(isA(String.class), capture(cap3)));
mockProcessor.process(and(isA(String.class), capture(cap4)));
replay(mockProcessor);
component.doSomething();
//check what values were passed to the mock
assertEquals("one", cap1.getValue());
assertEquals("two", cap2.getValue());
assertEquals("three", cap3.getValue());
assertEquals("four", cap4.getValue());
verify(mockProcessor);
}
}
(Please note that this is just a simplified test case - I know that I could specify the exact value of the arguments I expect passed to my mock, but in my real case the arguments are complex objects with a handful of fields, and I want to capture the object so I can assert against just a few of those fields without re-creating the entire object in my test case).
When I run the test, it fails at:
junit.framework.ComparisonFailure: expected:<[one]> but was:<[four]>
Meaning that the parameter that EasyMock is capturing in cap1 is not the first call to the method, but the last (since the value is four). I get the same results if I reverse the captures() declarations, i.e. use cap4 with the first method call, etc.
This seems like it might be a bug within EasyMock - different parameters passed to the same method in different invocations don't seem to be capture correctly.
Is anyone else using capture() with EasyMock and having similar problems? Is there an easy workaround you know of, or a different way I can capture the parameters being passed to my mock's methods?
Update 1: fixed code sample to show I am using createMock, not createStrictMock, but I get the same error with both (although the actual value of what is captured changes).
I've received an answer on the bug I submitted to the Easymock sourceforge site, and a developer has confirmed it is indeed a bug with this version of Easymock.
It is indeed a bug. The capture is done even if it was already done. The
current workaround is to implement your own capture object and override
setValue to do this:
#Override
public void setValue(T value) {
if(!hasCaptured()) {
super.setValue(value);
}
}
I was playing around with your test and could not solve.
However I extended the Capture Class to see if the values were set in a different order (I was suspicious that EasyMock internally was using a hash with a key generated from the methods and the parameters) I was wrong the methods are set in the correct order. But there is something really weird going on.. It seems that the algorithm does some kind assigning pattern.. Well let me show the code and the strange output.... BTW the changes from mock, niceMock and strictMock didn't make anydifference..
class MyCapture extends Capture<String> {
private String id;
public MyCapture(String id) {
super();
System.out.printf("Constructor %s expecting %s\n", id, this.getClass().getName());
this.id = id;
}
private static final long serialVersionUID = 1540983654657997692L;
#Override
public void setValue(String value) {
System.out.printf("setting value %s expecting %s \n", value, id);
super.setValue(value);
}
#Override
public String getValue() {
System.out
.printf("getting value %s expecting %s \n", super.getValue(), id);
return super.getValue();
}
}
public void testCapture() {
// create the mock, wire it up
Processor mockProcessor = createStrictMock(Processor.class);
Component component = new Component(mockProcessor);
// we're going to call the process method four times
// with different arguments, and we want to capture
// the value passed to the mock so we can assert against it later
Capture<String> cap1 = new MyCapture("A");
Capture<String> cap2 = new MyCapture("B");
Capture<String> cap3 = new MyCapture("C");
Capture<String> cap4 = new MyCapture("D");
mockProcessor.process(and(isA(String.class), capture(cap1)));
mockProcessor.process(and(isA(String.class), capture(cap2)));
mockProcessor.process(and(isA(String.class), capture(cap3)));
mockProcessor.process(and(isA(String.class), capture(cap4)));
replay(mockProcessor);
component.doSomething();
// check what values were passed to the mock
assertEquals("A", cap1.getValue());
assertEquals("B", cap2.getValue());
assertEquals("C", cap3.getValue());
assertEquals("D", cap4.getValue());
verify(mockProcessor);
}
}
*And this is the output *
Constructor A expecting com.comp.core.dao.impl.CaptureTest$MyCapture
Constructor B expecting com.comp.core.dao.impl.CaptureTest$MyCapture
Constructor C expecting com.comp.core.dao.impl.CaptureTest$MyCapture
Constructor D expecting com.comp.core.dao.impl.CaptureTest$MyCapture
calling process A
setting value A expecting A
calling process B
setting value B expecting A <<Setting the wrong guy
setting value B expecting A <<Setting the wrong guy
setting value B expecting B <<Ops this is the right one..stop
calling process C
setting value C expecting B <<Setting the wrong guy
setting value C expecting B <<Setting the wrong guy
setting value C expecting C <<Setting the wrong guy
calling process D
setting value D expecting C <<Setting the wrong guy
setting value D expecting C <<Setting the wrong guy
setting value D expecting D <<Ops this is the right one..stop
getting value B expecting A
Sorry I can't help you more. It might be indeed a bug in easy mock.
You can also try using EasyMock.createNiceMock(...) instead of EasyMock.createStrictMock(...) or EasyMock.createMock(...).
Although, I agree that it looks more like a bug with createMock.
This is a problem more appropriate for state-based testing, I think.
With JMockit, you could solve it like this:
import mockit.*;
import static mockit.Mockit.*;
import mockit.integration.junit3.*;
public class CaptureTest extends JMockitTestCase
{
interface Processor { void process(String x); }
class Component
{
private final Processor processor;
private final String[] s = {"one", "two", "three", "four"};
Component(Processor processor) { this.processor = processor; }
public void doSomething()
{
for (String value : s) {
processor.process(value);
}
}
}
#MockClass(realClass = Processor.class)
static class MockProcessor
{
private final String[] expectedValues;
private int i;
MockProcessor(String... expectedValues) { this.expectedValues = expectedValues; }
#Mock
void process(String x)
{
assertEquals(expectedValues[i++], x);
}
}
public void testCapture()
{
Processor mockProcessor = setUpMock(new MockProcessor("one", "two", "three", "four"));
Component component = new Component(mockProcessor);
component.doSomething();
}
}
In short, here's what worked for me:
MyClass myMock = EasyMock.createStrictMock(MyClass.class);
...
EasyMock.checkOrder(myMock, true); // before the capture and verify, not sure if it matters
...
Capture<MyArg> capturedArgs = new Capture<MyArg>();
expect(myMock.search(capture(capturedArgs))).andReturn(someRandomReturn);
PS: I'm using EasyMock 3.0
Instead of calling EasyMock.createStrictMock(...) just call EasyMock.createMock(...). Should solve your problems.

Categories

Resources