Skip to content

Commit

Permalink
[#1] Now you can set properties of your superclass
Browse files Browse the repository at this point in the history
DON'T DO THIS AT HOME!
  • Loading branch information
marcingrzejszczak committed Dec 15, 2014
1 parent 6ab196b commit 5c95e17
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 14 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -16,7 +16,7 @@ buildscript {


group = 'com.blogspot.toomuchcoding'
version = '0.0.2-SNAPSHOT'
version = '1.0.0-SNAPSHOT'

This comment has been minimized.

Copy link
@szpak

szpak Dec 15, 2014

Contributor

Respect ;).

This comment has been minimized.

Copy link
@marcingrzejszczak

marcingrzejszczak Dec 15, 2014

Author Owner

I want to be like Firefox ;)

description = "Spock subjects-collaborators extension"

repositories {
Expand Down
@@ -1,11 +1,12 @@
package com.blogspot.toomuchcoding.spock.subjcollabs

import groovy.transform.PackageScope
import org.spockframework.runtime.model.FieldInfo
import spock.lang.Specification

import java.lang.reflect.Field


@PackageScope
interface Injector {
boolean tryToInject(Collection<Field> injectionCandidates, Specification specInstance, FieldInfo fieldInfo)
}
@@ -1,5 +1,4 @@
package com.blogspot.toomuchcoding.spock.subjcollabs

import groovy.transform.PackageScope
import org.codehaus.groovy.reflection.ClassInfo
import org.spockframework.runtime.model.FieldInfo
Expand All @@ -15,22 +14,21 @@ class PropertyInjector extends NonConstructorBasedInjector {
// Field injection; mocks will first be resolved by type, then, if there is several property of the same type, by the match of the field name and the mock name.
// Note 1: If you have fields with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen.
Object subject = instantiateSubjectAndSetOnSpecification(specInstance, fieldInfo)
List<Field> fields = getAllFieldsFromSubject(fieldInfo)
List<Field> fields = getAllFieldsFromSubject(fieldInfo.type, [])
Map matchingFields = getMatchingFieldsBasingOnTypeAndPropertyName(injectionCandidates, fields)
matchingFields.each { Field field, Field injectionCandidate ->
subject[injectionCandidate.name] = specInstance[injectionCandidate.name]
field.set(subject, specInstance[injectionCandidate.name])
}
}

private List<Field> getAllFieldsFromSubject(FieldInfo fieldInfo) {
List<Field> fields = []
fieldInfo.type.declaredFields.each { Field field ->
if( !field.isSynthetic()
&& field.type != ClassInfo) {
fields << field
}
private List<Field> getAllFieldsFromSubject(Class type, List<Field> fields) {
fields.addAll(type.declaredFields.findAll { Field field -> !field.isSynthetic() && field.type != ClassInfo })
if (type.superclass != null) {
fields.addAll(getAllFieldsFromSubject(type.superclass, fields))
}
fields*.setAccessible(true)
return fields

}

private Map getMatchingFieldsBasingOnTypeAndPropertyName(Collection<Field> injectionCandidates, List<Field> allFields) {
Expand Down
Expand Up @@ -31,6 +31,9 @@ import java.lang.reflect.Field
*
* Field injection; collaborators will first be resolved by type, then, if there is several property of the same type, by the match of the field name and the mock name.
*
* @since 1.0.0 - allows a hideous hack that you can inject values to Subject's superclass' private properties.
* Please do not ever use it unless you're really desperate.
*
* Note 1: If you have fields with the same type (or same erasure), it's better to name all {@link Collaborator @Collaborator} annotated fields with the matching fields, otherwise injection might not happen.
* Note 2: If {@link Subject @Subject} instance wasn't initialized before and have a no-arg constructor, then it will be initialized with this constructor.
*/
Expand Down
@@ -1,7 +1,5 @@
package com.blogspot.toomuchcoding.spock.subjcollabs

import com.blogspot.toomuchcoding.spock.subjcollabs.Collaborator
import com.blogspot.toomuchcoding.spock.subjcollabs.Subject
import spock.lang.Specification

class PropertiesInjectionSpec extends Specification {
Expand Down
@@ -0,0 +1,66 @@
package com.blogspot.toomuchcoding.spock.subjcollabs
import spock.lang.Specification

import java.lang.reflect.Field

class PropertiesInjectionWithSuperclassSpec extends Specification {

public static final String TEST_METHOD_1 = "Test method 1"

public static final String TEST_METHOD_2 = "Test method 2"

@Collaborator
SomeOtherClass someOtherClassToBeInjected = Mock()

@Collaborator
SomeOtherClass someOtherClass = Mock()

@Subject
SomeClass systemUnderTest

This comment has been minimized.

Copy link
@szpak

szpak Dec 15, 2014

Contributor

Something wrong with formatting?

This comment has been minimized.

Copy link
@marcingrzejszczak

marcingrzejszczak Dec 15, 2014

Author Owner

Formatting is the least of the issues here - honestly I feel bad with this code :D Especially with setAccessible(true) stuff ;)

This comment has been minimized.

Copy link
@szpak

szpak Dec 15, 2014

Contributor

Ehh, I treat those hacks as something normal in that hackery project :).

This comment has been minimized.

Copy link
@szpak

szpak Dec 15, 2014

Contributor

And as a good revierer I have to report at least one remark ;).


def "should inject collaborator into subject via properties"() {
given:
Field someOtherClassField = systemUnderTest.class.superclass.declaredFields.find {it.name == 'someOtherClass'}

This comment has been minimized.

Copy link
@szpak

This comment has been minimized.

Copy link
@marcingrzejszczak

marcingrzejszczak Dec 15, 2014

Author Owner

Yeah I had it on my previous operating system but I forgot to redo it on this one. Thanks for reminding me though - will do it now.

Field someOtherClassToBeInjectedField = systemUnderTest.class.superclass.declaredFields.find {it.name == 'someOtherClassToBeInjected'}
someOtherClassField.accessible = true
someOtherClassToBeInjectedField.accessible = true

and:
SomeOtherClass injectedSomeOtherClass = someOtherClassField.get(systemUnderTest)
SomeOtherClass injectedSomeOtherClassToBeInjected = someOtherClassToBeInjectedField.get(systemUnderTest)

and:
someOtherClass.someMethod() >> TEST_METHOD_1
someOtherClassToBeInjected.someMethod() >> TEST_METHOD_2

when:
String firstResult = injectedSomeOtherClass.someMethod()
String secondResult = injectedSomeOtherClassToBeInjected.someMethod()

then:
firstResult == TEST_METHOD_1
injectedSomeOtherClass == someOtherClass

and:
secondResult == TEST_METHOD_2
injectedSomeOtherClassToBeInjected == someOtherClassToBeInjected
}


class SomeClassParent {
private SomeOtherClass someOtherClass
private SomeOtherClass someOtherClassToBeInjected
}

class SomeClass extends SomeClassParent {
}

class SomeOtherClass {
String someMethod() {
"Some other class"
}
}

}


0 comments on commit 5c95e17

Please sign in to comment.