FakeRequest seem to be Null when passed to a controller in unit test - mockito

Getting Null Pointer error when unit testing controller. The issue seem to be in the line
def signupUser = Action.async{
implicit request => { //requeust seem to be null
I suspect so because the stacktrace from previous tests point to implicit request line. But I don’t know what could be wrong in this because I am using FakeRequest like so val request = FakeRequest("POST", "/ws/users/signup").withJsonBody(Json.parse("""{"bad": "field"}"""))
Following is a snippet of a controller I want to unit-test
class UserController #Inject()(userRepo: UsersRepository,cc: ControllerComponents, silhouette: Silhouette[JWTEnv])(implicit exec: ExecutionContext) extends AbstractController(cc){
def signupUser = Action.async{
implicit request => {...}
}
I only want to test that the controller returns an error when it gets a request without json body. Thus I don't need Silhouette and I want to mock it. But I am getting null pointer error.
Following is the way I have written my unit test case is
class UserControllerUnitSpec extends PlaySpec with MockitoSugar {
"User signup request with non-JSON body" should {
"return 400 (Bad Request) and the validation text 'Incorrect body type. Body type must be JSON'" in {
val email = "d#d.com"
val loginInfo = LoginInfo(CredentialsProvider.ID, email);
val passwordInfo = PasswordInfo("someHasher","somePassword",Some("someSalt"))
val internalUserProfile = InternalUserProfile(loginInfo,true,Some(passwordInfo))
val externalUserProfile = ExternalUserProfile(email,"d","d",Some("somePassword"))
val userProfile = UserProfile(Some(internalUserProfile),externalUserProfile)
val user = User(UUID.randomUUID(),userProfile)
println("testing with mocked User value",user);
val mockUserRepository = mock[UsersRepository]
when(mockUserRepository.findUser(loginInfo)).thenReturn(Future(Some(user)))
when(mockUserRepository.saveUser(user)).thenReturn(Future(Some(user)))
val mockSilhouette = mock[Silhouette[JWTEnv]] //I am probably not doing this correctly
val mockControllerComponents = mock[ControllerComponents] //I am not sure if this is correct either
val controller = new UserController(mockUserRepository,mockControllerComponents,mockSilhouette)
val result:Future[Result] = controller.signupUser(FakeRequest())
(result.map(response => {
println("response: ",response)
response mustBe BadRequest
}))
}
}
}

Regarding mockControllerComponents, Helpers.stubControllerComponents can be used instead of a mock:
val mockControllerComponents = Helpers.stubControllerComponents()
Regarding mockSilhouette, you have to setup the mock using when(...).thenReturn(...) similarly to how you have done it formockUserRepository, that is, inspect all the usages of silhouette inside signupUser and provide the appropriate method stubs:
val mockSilhouette = mock[Silhouette[JWTEnv]]
when(mockSilhouette.foo(...)).thenReturn(...)
when(mockUserRepository.bar(...)).thenReturn(...)
...

(Posted solution on behalf of the question author).
Here is the answer which worked. Thanks Mario.
class UserControllerUnitSpec extends PlaySpec /*with MockitoSugar*/ {
"User signup request with non-JSON body" should {
"return 400 (Bad Request) and the validation text 'Incorrect body type. Body type must be JSON'" in {
val email = "d#d.com"
val loginInfo = LoginInfo(CredentialsProvider.ID, email);
val passwordInfo = PasswordInfo("someHasher","somePassword",Some("someSalt"))
val internalUserProfile = InternalUserProfile(loginInfo,true,Some(passwordInfo))
val externalUserProfile = ExternalUserProfile(email,"d","d",Some("somePassword"))
val userProfile = UserProfile(Some(internalUserProfile),externalUserProfile)
val user = User(UUID.randomUUID(),userProfile)
println("testing with mocked User value",user);
val mockUserRepository = mock(classOf[UsersRepository])
// when(mockUserRepository.findUser(loginInfo)).thenReturn(Future(Some(user)))
// when(mockUserRepository.saveUser(user)).thenReturn(Future(Some(user)))
// val mockSilhouette = mock(classOf[Silhouette[JWTEnv]])
val mockControllerComponents = Helpers.stubControllerComponents()//mock(classOf[ControllerComponents])
/*
The controller needs Silhouette. Using Silhouette's test kit to create fake instances.
If you would like to test this controller, you must provide an environment that can handle your Identity and Authenticator implementation.
For this case Silhouette provides a FakeEnvironment which automatically sets up all components needed to test your specific actions.
You must only specify one or more LoginInfo -> Identity pairs that should be returned by calling request.identity in your action and
the authenticator instance that tracks this user.
*/
//User extends Identity trait
/*
Under the hood, the environment instantiates a FakeIdentityService which stores your given identities and returns it if needed.
It instantiates also the appropriate AuthenticatorService based on your defined Authenticator type. All Authenticator services are real
service instances set up with their default values and dependencies.
*/
implicit val sys = ActorSystem("MyTest")
implicit val mat = ActorMaterializer()
implicit val env = FakeEnvironment[JWTEnv](Seq(loginInfo->user))
val defaultParser = new mvc.BodyParsers.Default()
val securedAction = new DefaultSecuredAction(new DefaultSecuredRequestHandler(new DefaultSecuredErrorHandler(stubMessagesApi())),defaultParser)
val unsecuredAction = new DefaultUnsecuredAction(new DefaultUnsecuredRequestHandler(new DefaultUnsecuredErrorHandler(stubMessagesApi())),defaultParser)
val userAware = new DefaultUserAwareAction(new DefaultUserAwareRequestHandler(),defaultParser)
val mockSilhouette = new SilhouetteProvider[JWTEnv](env,securedAction,unsecuredAction,userAware)
val controller = new UserController(mockUserRepository,mockControllerComponents,mockSilhouette)
val request = FakeRequest("POST","ws/users/signup")
println("sending request",request)
//val result = controller.someMethod()
val result:Future[Result] = controller.signupUser(request)
status(result) mustBe BAD_REQUEST
}
}
}

Related

Gatling Rest API Testing - retrieve a value from json response and add it to the list, iterate through list

I am new to Gatling, I am trying to do the performance testing for couple of rest calls. In my scenario I need to extract a value from json response of the 1st call and add those values to the list after looping for few times. Again after looping for few times and adding the values into the list, I want to reuse each value in my next rest call by iterating over the values in the list. Can anyone please suggest on how to implement this. I tried something as below,
var datasetIdList = List.empty[String]
val datasetidsFeeder = datasetIdList.map(datasetId => Map("datasetId" -> datasetId)).iterator
def createData() = {
repeat(20){
feed("").exec(http("create dataset").post("/create/data").header("content-type", "application/json")
.body(StringBody("""{"name":"name"}"""))
.asJson.check(jsonPath("$.id").saveAs("userId"))))
.exec(session => { var usrid = session("userId").as[String].trim
datasetIdList:+= usrid session})
}}
def upload()= feed(datasetidsFeeder).exec(http("file upload").post("/compute-metaservice/datasets/${datasetId}/uploadFile")
.formUpload("File","./src/test/resources/data/File.csv")
.header("content-type","multipart/form-data")
.check(status is 200))
val scn = scenario("create data and upload").exec(createData()).exec(upload())
setUp(scn.inject(atOnceUsers(1))).protocols(httpConf)
}
I am seeing an exception that ListFeeder is empty when trying to run above script. Can someone please help
Updated Code:
class ParallelcallsSimulation extends Simulation{
var idNumbers = (1 to 50).iterator
val customFeeder = Iterator.continually(Map(
"name" -> ("test_gatling_"+ idNumbers.next())
))
val httpConf = http.baseUrl("http://localhost:8080")
.header("Authorization","Bearer 6a4aee03-9172-4e31-a784-39dea65e9063")
def createDatasetsAndUpload() = {
repeat(3) {
//create dataset
feed(customFeeder).exec(http("create data").post("/create/data").header("content-type", "application/json")
.body(StringBody("""{ "name": "${name}","description": "create data and upload file"}"""))
.asJson.check(jsonPath("$.id").saveAs("userId")))
.exec(session => {
val name = session("name").asOption[String]
println(name.getOrElse("COULD NOT FIND NAME"))
val userId = session("userId").as[String].trim
println("%%%%% User ID ====>"+userId)
val datasetIdList = session("datasetIdList").asOption[List[_]].getOrElse(Nil)
session.set("datasetIdList", userId :: datasetIdList)
})
}
}
// File Upload
def fileUpload() = foreach("${datasetIdList}","datasetId"){
exec(http("file upload").post("/uploadFile")
.formUpload("File","./src/test/resources/data/File.csv")
.header("content-type","multipart/form-data")
.check(status is 200))
}
def getDataSetId() = foreach("${datasetIdList}","datasetId"){
exec(http("get datasetId")
.get("/get/data/${datasetId}")
.header("content-type","application/json")
.asJson.check(jsonPath("$.dlp.dlp_job_status").optional
.saveAs("dlpJobStatus")).check(status is 200)
).exec(session => {
val datastId = session("datasetId").asOption[String]
println("request for datasetId >>>>>>>>"+datastId.getOrElse("datasetId not found"))
val jobStatus = session("dlpJobStatus").asOption[String]
println("JOB STATUS:::>>>>>>>>>>"+jobStatus.getOrElse("Dlp Job Status not Found"))
println("Time: >>>>>>"+System.currentTimeMillis())
session
}).pause(10)
}
val scn1 = scenario("create multiple datasets and upload").exec(createDatasetsAndUpload()).exec(fileUpload())
val scn2 = scenario("get datasetId").pause(100).exec(getDataSetId())
setUp(scn1.inject(atOnceUsers(1)),scn2.inject(atOnceUsers(1))).protocols(httpConf)
}
I see below error when I try to execute above script
[ERROR] i.g.c.s.LoopBlock$ - Condition evaluation crashed with message 'No attribute named 'datasetIdList' is defined', exiting loop
var datasetIdList = List.empty[String] defines a mutable variable pointing to a immutable list.
val datasetidsFeeder = datasetIdList.map(datasetId => Map("datasetId" -> datasetId)).iterator uses the immutable list. Further changes to datasetIdList is irrelevant to datasetidsFeeder.
Mutating a global variable with your virtual user is usually not a good idea.
You can save the value into the user's session instead.
In the exec block, you can write:
val userId = session("userId").as[String].trim
val datasetIdList = session("datasetIdList").asOption[List[_]].getOrElse(Nil)
session.set("datasetIdList", userId :: datasetIdList)
Then you can use foreach to iterate them all without using a feeder at all.
foreach("${datasetIdList}", "datasetId") {
exec(http("file upload")
...
}
You should put more work in your question.
Your code is not syntax-highlighted, and is formatted poorly.
You said "I am seeing an exception that ListFeeder is empty" but the words "ListFeeder" are not seen anywhere.
You should post the error message so that it's easier to see what went wrong.
In the documentation linked, there is a Warning. Quoted below:
Session instances are immutable!
Why is that so? Because Sessions are messages that are dealt with in a multi-threaded concurrent way, so immutability is the best way to deal with state without relying on synchronization and blocking.
A very common pitfall is to forget that set and setAll actually return new instances.
This is why the code in the updated question doesn't update the list.
session => {
...
session.set("datasetIdList", userId :: datasetIdList)
println("%%%% List =====>>>" + datasetIdList.toString())
session
}
The updated session is simply discarded. And the original session is returned in the anonymous function.

Failed to obtain broadcast value

I create a spark application like below.
When run with local client mode, everything goes fine.
But when I submit into YARN with cluster deploy mode on prod environment, variable applicationAction in last match block always be null.
So is there any problem which I'm using broadcast here, or there's any other method I could pass the variables to the last match case block.
Thanks.
object SparkTask {
private sealed trait AppAction {}
case class Action1() extends AppAction
case class Action2() extends AppAction
def main(args: Array[String]): Unit = {
var applicationAction: Broadcast[AppAction] = null
val sparkSession = SparkSession.builder.appName("SparkTask").getOrCreate
args(0) match {
case "action-1" => applicationAction = sparkSession.sparkContext.broadcast(Action1())
case "action-2" => applicationAction = sparkSession.sparkContext.broadcast(Action2())
case _ => sys.exit(255)
}
// Here goes some df action and get a persisted dataset
val df1 = ...
val df2 = ...
val df3 = ...
applicationAction.value match {
case Action1() => handleAction1(df3)
case Action2() => handleAction2(df3)
}
}
}
The purpose of broadcast variables it to share some data with executors.
I think in your use-case there are two possibilites:
You're trying to get some information from executors to driver: for this you shouldn't use broadcast variables but accumulators or something like take/collect.
You want take a decision based on applicationAction.value (immutable): in this case you can then use directly the value of args(0).

Should I pass my APIKEY in every request I do with Retrofit?

I'd like to know the best way to put my APIKEY in all my REST requests withtout having to add it in parameters of the request.
For now I just have a couple of calls, but I'm trying to see further.
#GET(".")
fun getSearch(#Query("s") text: String, #Query("apikey") APIKEY: String) : Observable<ResponseSearch>
I was wondering if there was a way not to have the APIKEY in variables of every call
You can, but the better solution is to use Okhttp Interceptors
Here's an example:
class TokenInterceptor(private val preferencesStorage: SharedPreferencesStorage) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var original = chain.request()
val token = preferencesStorage.getAccessToken()
val url = original.url().newBuilder().addQueryParameter("apikey", token).build()
original = original.newBuilder().url(url).build()
return chain.proceed(original)
}
}
You should also add TokenInterceptor to your Okhttp client builder
val client = OkHttpClient.Builder()
.addInterceptor(TokenInterceptor(SharedPreferencesStorage()))
.build()
It's better to have a middleware for your requests, where all queries passed through it, and there you will have a single point of adding the APIkey, also i would prefer it to add it to the header instead of query parameters
More "modern" way to achieve it
private fun apiKeyAsQuery(chain: Interceptor.Chain) = chain.proceed(
chain.request()
.newBuilder()
.url(chain.request().url.newBuilder().addQueryParameter("api-key", ApiKey).build())
.build()
)
private fun apiKeyAsHeader(it: Interceptor.Chain) = it.proceed(
it.request()
.newBuilder()
.addHeader("api-key", ApiKey)
.build()
)
...
.client(
OkHttpClient.Builder()
.addInterceptor { apiKeyAsQuery(it) }
// Or
.addInterceptor { apiKeyAsHeader(it) }
.build()
)
...

Mockito returning null value for Future

I am unit-testing a controller in a play-framework application.
The controller uses a repository and I am mocking the repository as follows
val mockUserRepository = mock(classOf[UsersRepository])
when(mockUserRepository.findOne(userKeys)).thenReturn(Future{Some(user)})
when(mockUserRepository.save(user)).thenReturn(Future(Some(user)))
Then I run the following test. In the test, controller.signupUser(request) calls the findOne method of the repository as follows
val findUserFuture: Future[Option[User]] = userRepo.findOne(userKeys)
println("user future is ",findUserFuture)
findUserFuture.flatMap { (userOption: Option[User]) => //this crashes because findUserFuture is null)
But findOne returns a null instead of a dummy Future{user}
"User signup request with body" should {
"return OK and user profile if the signup profile data is correct" in {
val jsonBody = Json.parse(
"""
{
"external-profile":{
"email":"test#test.com",
"firstname":"fn",
"lastname":"ln",
"password":"aA1!1111"
}
}
""")
//val jsonBody = Json.toJson(signupInfo)
val request: Request[AnyContentAsJson] = FakeRequest("POST", "ws/users/signup",Headers(("someH"->"someV")),new AnyContentAsJson(jsonBody))
println("sending sign up request ", request)
//request.body = signupInfo
val response: Future[Result] = controller.signupUser(request)
val responseBodyAsJsValue:JsValue = contentAsJson(response)
println("received response of sign up ", responseBodyAsJsValue)
}
}
error
received profile
UserProfile(None,ExternalUserProfile(test#test.com,fn,ln,Some(aA1!1111)))
checking if the user with the following details exists LoginInfo(credentials,test#test.com)
returning id 116 for name test#test.com
(user future is ,null)
java.lang.NullPointerException was thrown.
java.lang.NullPointerException
at controllers.UserController.$anonfun$signupUser$1(UserController.scala:116)
What I might be doing wrong?
The issue apparently was the I wasn't using when correctly.
I read that "
Mockito allows to configure the return values of its mocks via a fluent API. Unspecified method calls return "empty" values:
null for objects
0 for numbers
false for boolean
empty collections for collections
Mocks can return different values depending on arguments passed into a method. The when(…​.).thenReturn(…​.) method chain is used to specify a a return value for a method call with pre-defined parameters.
"
when expects the method as well as the exact arguments. So if I want to call findUser(userkey) where userkey's value is say 1 in the actual call then I need to write when(findUser(1)) or userKey=1; findUser(userKey)).
In my wrong implementation, I had set userkey as
UserKeys(1,"test#test.com",loginInfo,"","")
but the call to findUser was with value
UserKeys(116,"d#d.com",loginInfo,"fn","ln")
I changed the userkey value in test and it worked
val userKeys = UserKeys(utilities.bucketIDFromEmail(email)/*returns 116*/,"d#d.com",loginInfo,"fn","ln")
when(mockUserRepository.findOne(userKeys)).thenReturn(Future{Some(user)})
when(mockUserRepository.save(user)).thenReturn(Future(Some(user)))

How to obtain virtual user id/details in gatling?

I am new to Gatling and Scala and I need your advice.
I would like to obtain load test for n-users. Each user have to send request for creating different accounts. This is obtained by sending json file with appropriate array of objects ('entries' in our case).
Each single user must send different login as our backend system is checking if username is unique. Somehow we have to be sure that gatling is sending different data for each virtual user and also for each entries as well.
We noticed that there us session element which represents virtual user's state. Problem is that code showed below will not work as Exec structure used with expression function does not send any request.
There is section that could work but I do not know how to determine third parameter to distinguish virtual user id. Please find below simple json file structure used for this test
{
"entries": [
{
"userName": "some user name",
"password": "some password"
}
}
and scala code with my comments
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class UserCreationTest extends Simulation {
val profilesNumber = 2
val virtualUsers = 2
val httpConf = http
.baseURL("some url")
.acceptHeader("application/json")
.basicAuth("username", "password")
// This method will multiply 'entries' section in JSON 'entriesNumber' times
def createJsonUserEntries(entriesNumber: Int, users: List[String], userId : Long): String = {
val header = """{"entries": ["""
val footer = """]}"""
val builder = StringBuilder.newBuilder
for (i <- 0 until entriesNumber) {
val userIndex = (userId.toInt - 1) * entriesNumber + i
val userName = users(userIndex).get
val apiString =
s"""{
"userName": "${userName}"
"password": "password"
}"""
builder.append(apiString)
if (i != entriesNumber) {
builder.append(",")
}
}
header + builder.toString() + footer
}
// We do have method for generating user names based on profilesNumber and virtualUsers variables
// but for sake of this example lets hardcode 4 (profilesNumber * virtualUsers) user names
val usersList = List("user-1", "user-2", "user-3", "user-4")
//This will throw exception as no request was send. According to documentation function block is used to debugging/editing session
val scn = scenario("Create WiFi User Profile")
.exec(session => {
http("CreateUserProfile")
.post("/userProfiles/create/")
.body(StringBody(
createJsonUserEntries(profilesNumber, userslList, session.userId).toString
)
).asJSON
session})
// This exec block will send a request but I do not know how to determine third param that should be virtual user Id
// To run this section please comment previous whole scenario block
/*
val scn = scenario("")
.exec(http("CreateUserProfile")
.post("/userProfiles/create/")
.body(StringBody(
createJsonUserEntries(profilesNumber, emailList, ???).toString
)
).asJSON
)
*/
setUp(scn.inject(atOnceUsers(virtualUsers)).protocols(httpConf))
}
Can you help me on that please? Is there any other way to do that in gatling? Thank you very much in advance
so you are trying to have each user have a unique userId?
you could create a feeder that does this
var userIdFeeder = (1 to 999999).toStream.map(i => Map("userId" -> i)).toIterator
val scn = scenario("")
.feed(userIdFeeder)
.exec(http("CreateUserProfile")
.post("/userProfiles/create/")
.body(StringBody(
createJsonUserEntries(profilesNumber, emailList, "${userId}").toString
)
).asJSON
)

Resources