jMock

Expectations "a" and "an" are deprecated, but there is no adequate substitute

Details

  • Type: Bug Bug
  • Status: Open Open
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • Environment:
    Java 6
  • Number of attachments :
    0

Description

The deprecated documentation over Expectations "a" and "an" suggests using "any" or "aNonNull" as replacements. However, "a" and "an" check that the type of the actual parameter passed in matches the prescribed type whereas "any" and "aNonNull" do not.

I have a method foo.schedule(Event event) where I need my Expectation to check that a specific subclass of Event was passed in when this method is called.

For example, if there are two subclasses of Event, StartEvent and EndEvent, if you set an expectation of:

oneOf(foo).schedule((Event)with(a(StartEvent.class)));

then if you call schedule with an EndEvent, it will fail as expected.

However, if you set an expectation of

oneOf(foo).schedule(with(any(StartEvent.class)));

or

oneOf(foo).schedule(with(aNonNull(StartEvent.class)));

then if you call schedule with an EndEvent, then all expectations will pass.

Activity

Hide
Ken Stevens added a comment -

Suggested temporary resolution:

Do not make a() and an() deprecated.

Suggested long term resolution (if possible):
add the type-checking behaviour to any() and aNonNull()

Show
Ken Stevens added a comment - Suggested temporary resolution: Do not make a() and an() deprecated. Suggested long term resolution (if possible): add the type-checking behaviour to any() and aNonNull()
Hide
dan wilkin added a comment - - edited

I've recently introduced a workaround in our local project that seems to work very well:

	public static <T> Matcher<T> isA(Class<T> type) {
		return (Matcher<T>) new IsInstanceOf(type);
	}

I'm using it to replace org.jmock.Expectations.a(Class) and org.jmock.Expectations.an(Class) static methods in all uses since in some cases where a specific parameter type is expected (not Object) these two methods cause compilation errors (a and an return Object):

    public static Matcher<Object> a(Class<?> type) {
        return new IsInstanceOf(type);
    }

For example, this doesn't compile:

	one(mockEntityManager).createQuery(with(a(String.class)));

but this does:

	one(mockEntityManager).createQuery(with(isA(String.class)));

I'm also using it to replace uses of org.jmock.Expectations.aNonNull(Class). While the Generics type handling is working there, there is no type checking in the aNonNull(..) method. A class type is provided by the caller, but the method simply checks the nullity of the value. If aNonNull(Class) and org.jmock.Expectations.aNull(Class) are really only intended to check nullity, then these methods shouldn't receive arguments at all - the type argument isn't being used (no type coercion is required to evaluate nullity); resulting in aNonNull() and aNull(), where aNull() invokes a default instance of IsNull<T>:

    public static <T> Matcher<T> aNull() {
        return new IsNull<T>();
    }

I'm also using isA(Class) to replace occurrences of org.hamcrest.Matchers.instanceOf(Class) as this matcher method has the same problem as a(Class) and an(Class) but performs the desired nullity and type checking.

I don't recommend incorporating isA(Class) into JMock Expectations class because of it's method name. Instead, it would be easier to simply change a(Class) and an(Class) to:

    public static <T> Matcher<T> a(Class<T> type) {
        return (Matcher<T>) new IsInstanceOf(type);
    }

and

    public static <T> Matcher<T> an(Class<T> type) {
        return (Matcher<T>) new IsInstanceOf(type);
    }

I would recommend overloading aNonNull(Class) with aNonNull() and aNull(Class) with aNull(). As for the Hamcrest matcher, instanceOf() could be potentially revised to:

  public static <T> org.hamcrest.Matcher<T> instanceOf(java.lang.Class<T> type) {
    return org.hamcrest.core.IsInstanceOf.<T>instanceOf(type);
  }

Just some thoughts on what appears to be working very well for us, but I would rather get rid of the isA(..) mnemonic since it doesn't flow well where used: ...createQuery(with(isA(String.class))); I would prefer to use: ...createQuery(with(a(String.class)));

~dan

Show
dan wilkin added a comment - - edited I've recently introduced a workaround in our local project that seems to work very well:
	public static <T> Matcher<T> isA(Class<T> type) {
		return (Matcher<T>) new IsInstanceOf(type);
	}

I'm using it to replace org.jmock.Expectations.a(Class) and org.jmock.Expectations.an(Class) static methods in all uses since in some cases where a specific parameter type is expected (not Object) these two methods cause compilation errors (a and an return Object):
    public static Matcher<Object> a(Class<?> type) {
        return new IsInstanceOf(type);
    }
For example, this doesn't compile:
	one(mockEntityManager).createQuery(with(a(String.class)));
but this does:
	one(mockEntityManager).createQuery(with(isA(String.class)));
I'm also using it to replace uses of org.jmock.Expectations.aNonNull(Class). While the Generics type handling is working there, there is no type checking in the aNonNull(..) method. A class type is provided by the caller, but the method simply checks the nullity of the value. If aNonNull(Class) and org.jmock.Expectations.aNull(Class) are really only intended to check nullity, then these methods shouldn't receive arguments at all - the type argument isn't being used (no type coercion is required to evaluate nullity); resulting in aNonNull() and aNull(), where aNull() invokes a default instance of IsNull<T>:
    public static <T> Matcher<T> aNull() {
        return new IsNull<T>();
    }

I'm also using isA(Class) to replace occurrences of org.hamcrest.Matchers.instanceOf(Class) as this matcher method has the same problem as a(Class) and an(Class) but performs the desired nullity and type checking. I don't recommend incorporating isA(Class) into JMock Expectations class because of it's method name. Instead, it would be easier to simply change a(Class) and an(Class) to:
    public static <T> Matcher<T> a(Class<T> type) {
        return (Matcher<T>) new IsInstanceOf(type);
    }

and
    public static <T> Matcher<T> an(Class<T> type) {
        return (Matcher<T>) new IsInstanceOf(type);
    }

I would recommend overloading aNonNull(Class) with aNonNull() and aNull(Class) with aNull(). As for the Hamcrest matcher, instanceOf() could be potentially revised to:
  public static <T> org.hamcrest.Matcher<T> instanceOf(java.lang.Class<T> type) {
    return org.hamcrest.core.IsInstanceOf.<T>instanceOf(type);
  }

Just some thoughts on what appears to be working very well for us, but I would rather get rid of the isA(..) mnemonic since it doesn't flow well where used: ...createQuery(with(isA(String.class))); I would prefer to use: ...createQuery(with(a(String.class))); ~dan
Hide
Steve Freeman added a comment -

I think we should remove aNull(Thing.class) since it misleadingly suggests that the type is checked, which it isn't, it's really there to help the with() clause.

Show
Steve Freeman added a comment - I think we should remove aNull(Thing.class) since it misleadingly suggests that the type is checked, which it isn't, it's really there to help the with() clause.

People

Vote (2)
Watch (1)

Dates

  • Created:
    Updated: