I am working with a kotlin and spring project, Now I am trying to do the test of some service, which has some dependencies, I am having some problems, in order to get a success test. Maybe I my design is not good enough, moreover I have problems trying to call the method from the spy object, I am getting the issue: Cannot invoke real method 'getClubhouseFor' on interface based mock object. This is my code, Could you give me any idea about what I am doing bad.
Thanks in advance!!!!
This is my code:
import com.espn.csemobile.espnapp.models.UID
import com.espn.csemobile.espnapp.models.clubhouse.*
import com.espn.csemobile.espnapp.services.clubhouse.AutomatedClubhouseService
import com.espn.csemobile.espnapp.services.clubhouse.ClubhouseService
import com.espn.csemobile.espnapp.services.clubhouse.StaticClubhouseService
import com.espn.csemobile.espnapp.services.clubhouse.contexts.ClubhouseContext
import com.espn.csemobile.espnapp.services.core.CoreService
import rx.Single
import spock.lang.Specification
class ClubhouseServiceImplTest extends Specification {
StaticClubhouseService staticClubhouseService = GroovyStub()
AutomatedClubhouseService automatedClubhouseService = GroovyStub()
CoreService coreService = GroovyStub()
ClubhouseContext clubhouseContext = GroovyMock()
Clubhouse clubHouse
ClubhouseLogo clubhouseLogo
ClubhouseService spy = GroovySpy(ClubhouseService)
void setup() {
clubhouseLogo = new ClubhouseLogo("http://www.google.com", true)
clubHouse = new Clubhouse(new UID(), "summaryType", ClubhouseType.League, new ClubhouseLayout(), "summaryName", "MLB", clubhouseLogo, "http://www.google.com", "liveSportProp",new ArrayList<Integer>(), new ArrayList<ClubhouseSection>(),new ArrayList<ClubhouseAction>(), new HashMap<String, String>())
}
def "GetClubhouseFor"() {
given:
staticClubhouseService.getClubhouseFor(clubhouseContext) >> buildClubHouseMockService()
// The idea here is to get different responses it depends on the class of call.
automatedClubhouseService.getClubhouseFor(clubhouseContext ) >> buildClubHouseMockService()
spy.getClubhouseFor(clubhouseContext) >> spy.getClubhouseFor(clubhouseContext)
when:
def actual = spy.getClubhouseFor(clubhouseContext)
then:
actual != null
}
def buildClubHouseMockService(){
return Single.just(clubHouse)
}
}
The next are the classes involved in the test:
import com.espn.csemobile.espnapp.models.clubhouse.*
import com.espn.csemobile.espnapp.services.clubhouse.contexts.ClubhouseContext
import com.espn.csemobile.espnapp.services.core.CoreService
import org.springframework.context.annotation.Primary
import org.springframework.context.annotation.ScopedProxyMode
import org.springframework.stereotype.Service
import org.springframework.web.context.annotation.RequestScope
import rx.Single
interface ClubhouseService {
fun getClubhouseFor(context: ClubhouseContext): Single<Clubhouse?>
}
#Service
#RequestScope(proxyMode = ScopedProxyMode.NO)
#Primary
class ClubhouseServiceImpl(private val clubhouseContext: ClubhouseContext,
private var staticClubhouseService: StaticClubhouseService,
private var automatedClubhouseService: AutomatedClubhouseService,
private val coreService: CoreService?): ClubhouseService {
override fun getClubhouseFor(context: ClubhouseContext): Single<Clubhouse?> {
return staticClubhouseService.getClubhouseFor(clubhouseContext).flatMap { clubhouse ->
if (clubhouse != null) return#flatMap Single.just(clubhouse)
return#flatMap automatedClubhouseService.getClubhouseFor(clubhouseContext)
}
}
}
Well, first of all GroovySpy or GroovyStub do not make sense for Java or Kotlin classes because the special features of Groovy mocks are only available for Groovy classes. So don't expect to be able to mock constructors or static methods that way, if that was the reason for the usage. This is also documented here:
When Should Groovy Mocks be Favored over Regular Mocks? Groovy mocks should be used when the code under specification is written in Groovy and some of the unique Groovy mock features are needed. When called from Java code, Groovy mocks will behave like regular mocks. Note that it isn’t necessary to use a Groovy mock merely because the code under specification and/or mocked type is written in Groovy. Unless you have a concrete reason to use a Groovy mock, prefer a regular mock.
As for your problem with the spy, you cannot use a spy on an interface type. This is documented here:
A spy is always based on a real object. Hence you must provide a class type rather than an interface type, along with any constructor arguments for the type.
So either you just switch to Mock or Stub, both of which work on interface types, or you spy on the implementation class instead. In any case, my main suggestion is to read the documentation first and then try to use a new tool like Spock. My impression is that you have not used Spock before, but of course I could be wrong.
Related
Assume the following API endpoint controller
enum class AccessMethod {
SSO,
BASIC;
}
internal open class SomeController {
[...]
#PostMapping("{accessMethod}")
open fun trigger(#PathVariable("accessMethod", required = true) accessMethod: AccessMethod) {
logger.info {"Is arbitrary code execution possible here in spring? $accessMethod"}
}
}
I am not well aware of the validation mechanisms of Spring. Does the input get sanitised by default in the enum case a.k.a will Spring throw an error?
Malicious PoC
val malicious_payload = "\"} malicious() logger.info {\"Code Injection Successful"
client.post(endpoint_of_the_function_above + malicious_payload )
My first thought is that if the input value does not an ENUM value (string representation), it generates an error. So Enum enabled "SSO" and "BASIC" and throws everything else back.
Kotlin (like Java) is not a script language, so you can not use kotlin or java language directly for inject, because it have to build to bytecode and run on JVM. Thus, the operation cannot be manipulated.
Ofc, you can manipulate generate any query language (SQL, NoSQL or send message, etc) by which only travel through the system. Of course, the frameworks mainly handle it, but errors can always occur, right?
(Log4Shell was created because Log4J processing enabled external sources, so here it wasn't a case of code manipulated either.)
Example: improperly handled identifiers with which SQL queries are created.
I created a small project and used your "vuln" string. Just it use URL so this string converted to URL encoded.
It will say HTTP400 for any URL that is not /SSO and /BASIC. By definition, an /asdasd will also have an HTTP400 result.
\"} malicious() logger.info {\"Code Injection Successful to %5C%22%7D%20malicious%28%29%20logger.info%20%7B%5C%22Code%20Injection%20Successful
Request:
POST http://localhost:8080/%5C%22%7D%20malicious%28%29%20logger.info%20%7B%5C%22Code%20Injection%20Successful
Response:
{
"timestamp": "2022-08-25T12:51:23.092+00:00",
"path": "/%5C%22%7D%20malicious%28%29%20logger.info%20%7B%5C%22Code%20Injection%20Successful",
"status": 400,
"error": "Bad Request",
"requestId": "c3bf4acf-1"
}
Source (I mainly use the reactive environment, but this is not relevant now.)
package com.example.demo
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono
enum class AccessMethod {
SSO,
BASIC;
}
#SpringBootApplication
#RestController
class DemoApplication {
val logger: Logger = LoggerFactory.getLogger(this::class.java)
#PostMapping("{accessMethod}")
fun trigger(#PathVariable("accessMethod", required = true) accessMethod: AccessMethod): Mono<Void> {
logger.info("Is arbitrary code execution possible here in spring? $accessMethod")
return Mono.empty()
}
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
I've finally made some progress on Spring validation (on a JSON object coming in from RabbitMQ).
However there are a couple of things I don't understand:
In the documentation, it states I can just use the annotation #NotBlank then in my method I use the annotation #Valid. However I find this wasn't doing anything. So instead I did #field:NotBlank and it worked together with the following - why did this #field do the trick?
#JsonIgnoreProperties(ignoreUnknown = true)
data class MyModel (
#field:NotBlank(message = "ID cannot be blank")
val id : String = "",
#field:NotBlank(message = "s3FilePath cannot be blank")
val s3FilePath : String = ""
)
Then the function using this model:
#Service
class Listener {
#RabbitListener(queues = ["\${newsong.queue}"])
fun received(data: MyModel) {
val factory = Validation.buildDefaultValidatorFactory()
val validator = factory.validator
val validate = validator.validate(data)
// Then this `validate` will return an array of validation errors
println(validate)
}
}
Correct me if I'm wrong however I assumed just using #Valid and this point fun received(#Valid data: MyModel) it would just throw some exception for me to catch - any idea based on my code why this could have been?
Any advice/help would be appreciated.
Thanks.
Here are the imports:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import javax.validation.*
import org.springframework.amqp.rabbit.core.RabbitTemplate
import org.springframework.amqp.rabbit.annotation.RabbitListener
import javax.validation.constraints.NotBlank
Quoting Kotlin's documentation for annotations:
When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. To specify how exactly the annotation should be generated, use the following syntax:
class Example(#field:Ann val foo, // annotate Java field
#get:Ann val bar, // annotate Java getter
#param:Ann val quux) // annotate Java constructor parameter
So, until explicitly mention what you are annotating (field, getter or something else) in Kotlin class constructor, it won't automatically know where you want to put that annotation.
I'd like to test a Finagle Resolver properly.
Let's get a sample code:
import com.twitter.finagle.{Addr, Address, Resolver}
import com.twitter.util._
class DummyResolver extends Resolver {
override val scheme: String = "sample"
override def bind(arg: String): Var[Addr] = {
val delegate = SomeFactory.fromArgs(arg).build()
Var.async(Addr.Pending: Addr)(u => addrOf(u)(delegate))
}
}
The use of a static factory prevents me from unit-testing the resolver.
As far as I know, the only way to provide the resolver to Finagle is to declare it into the com.twitter.finagle.Resolver file in META-INF/services. Thus, I cannot provide an instance myself.
Given those constraints, how to design the resolver to either:
be able to provide an instance of the delegate,
or be able to properly test the behavior (and mock the delegate)?
Working on IDEA and trying to launch the following code:
package com.myCompany.routing.spring
import com.dropbox.core.DbxRequestConfig
import grails.util.Holders
import spock.lang.Specification
class DropboxSpringConfigSpec extends Specification {
def grailsApplication=Holders.grailsApplication
def "It instantiates and configures the dropboxRequestConfig component"() {
given:
def ctx = grailsApplication.mainContext
//do stuff...
}
}
I get the following error:
java.lang.NullPointerException: Cannot get property 'mainContext' on null object
at com.myCompany.routing.spring.DropboxSpringConfigSpec.It instantiates and configures the dropboxRequestConfig component(DropboxSpringConfigSpec.groovy:20)
I've recently made a pull on my VCS, so the code should work.
When running the test as Grails test, I get the following error:
Error |
2015-03-04 13:32:00,989 [localhost-startStop-1] ERROR context.GrailsContextLoader - Error initializing the application:
Missing configuration in Config.groovy: connection.uri.
Okay, it seems some configurations in Config.groovy were given the values of some environment variables:
elasticSearch {
connection {
uri = env.ES_URL
username = env.ES_USER
password = env.ES_PASSWORD
}
indexPrefix = 'test-'
}
Since I never created the corresponding environment variables, the GrailsContextLoader fails to find the corresponding value and the computation fail.
Initializing the required environmnent variables in my IDE and running the tests as Grails tests solved the problem.
By your extends Specification seems that what you have there is a unit test. grailsApplication is not available on unit specs, though it can be mocked (ie. using the #TestFor(ClassUnderTest) annotation mockes it up for you).
If what you want to test is configuration I would recommend writing an integration spec. On the integration phase, you basically have a wired grails application without the web interface. In that case all you'll need would be to do is
package com.myCompany.routing.spring
import com.dropbox.core.DbxRequestConfig
import grails.util.Holders
import grails.test.spock.IntegrationSpec
class DropboxSpringConfigSpec extends IntegrationSpec {
def grailsApplication //This will be auto-wired
def "It instantiates and configures the dropboxRequestConfig component"() {
given:
def ctx = grailsApplication.mainContext
//do stuff...
}
}
Re that test having worked previously in your VCS. I've never used Holders on a unit spec, so I cannot really say it wouldn't work, but it may have just been a false positive. From my understanding on the unit phase you don't have a running grails application and beans (including config) would not be available until mocked by you or the testing framework (again, using #TestFor or #Mock)
I am trying to close some test holes in my application and found that JaCoCo sonar plugin is giving me a smaller coverage in my enums because it thinks I should test the Package names.
Why is that?
It's showing me a 97% coverage in one of my enums and displaying a red line on top of the package declaration like this, telling me to test it... it does that in all Enums and on Enums only.
I came here looking for the answer to this, and after some more digging I discovered that it's due to some static methods that can be found in the bytecode of the compiled enum class which Jacoco is expecting to be covered. After some experimentation, I came up with the following superclass to use for unit tests which are focused on enums, with JUnit 4. This resolved my coverage problems with enums.
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static org.junit.Assert.assertEquals;
public abstract class EnumTest {
#Test
public void verifyEnumStatics() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class e = getEnumUnderTest();
Method valuesMethod = e.getMethod("values");
Object[] values = (Object[]) valuesMethod.invoke(null);
Method valueOfMethod = e.getMethod("valueOf", String.class);
assertEquals(values[0], valueOfMethod.invoke(null, ((Enum)values[0]).name()));
}
protected abstract Class getEnumUnderTest();
}
And then use it like this:
public class TravelTypeTest extends EnumTest {
#Override
protected Class getEnumUnderTest() {
return TravelType.class;
}
// other test methods if needed
}
This is a rough first attempt - it doesn't work on enums that for whatever reason don't have any entries, and doubtless there are better ways to get the same effect, but this will exercise the generated static methods by ensuring that you can retrieve the values of the enum, and that if you pass the name of the first enum entry to the valueOf() method you will get the first enum entry back.
Ideally we'd write a test that searches for all enums in the packages under test and exercise them in the same way automatically (and avoid having to remember to create a new test class for each new enum that inherits from EnumTest), but I don't have many enums so I haven't felt any pressure to attempt this yet.