I am writing unit tests for my play app. Some data in app is rarely changed needs to be accessed frequently. So, I am fetching it in start and storing it in a hashmap.
var inMemQuestion:Map[Long,QuestionSchema.Question] = {
println("Getting all questions from DB.....")
Await.result(listAll, Duration.Inf).map(a => a.id->a).toMap
}
In my methods I'm using data from here instead of db.
But when I try to mock this for unit testing my methods like this:
val sampleQsn = QuestionSchema.Question(9.toLong,"1 or 2",7.toLong,4.toLong,Some(1.0),Some(false),new java.sql.Timestamp(1599545742),new java.sql.Timestamp(1599552379))
val questionsRepo = mock[QuestionRepository]
when(questionsRepo.inMemQuestion(9)).thenReturn( // null pointer error in this line
sampleQsn
)
I'm getting null pointer error. Can anyone help me in resolving that?
Related
I'm trying to mock Http calls for unit test.
To do that I have done the following, I have created a RequestMock case class:
case class RequestMock() {
def sendRequest(httpRequest: HttpRequest)(implicit actorSystem: ActorSystem): Future[HttpResponse] = {
Http().singleRequest(httpRequest)
}
}
and in my service, I have written the following piece of code :
case class Service(requestHandler: RequestMock) {
....
for {
response <- {
requestHandler.sendRequest(
HttpRequest(
method = HttpMethods.GET,
uri = "http://database:9000"
)
)
} yield {
response
}
}
For the unit test, I'm trying to mock HttpCalls, to do that, I have done the following :
def test_2 = mock[RequestMock]
And for defining the mock behaviour I have done the following
when(test_2.sendRequest(
HttpRequest(
method = HttpMethods.GET,
uri = "http://database:9000")
)).thenReturn{
Future(
HttpResponse(
StatusCodes.OK,
entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`,"connection established"))
But, when I execute unit tests, I always get the following error:
java.lang.NullPointerException
Does anyone know how I can solve this issue ?
A couple of problems.
First of all, test_2 should be a val, not a def.
With def like you have it, you get a different instance every time you access it. So, you define the stub on one instance, but then create your Service with a different one, that does not have sendRequest defined, so returns null by default, and that causes your NPE.
The next problem, that you will probably encounter after you fix this one is that you are not defining all of the behavior.
when(test_2.sendRequest(
HttpRequest(
method = HttpMethods.GET,
uri = "http://database:9000")
))
Only creates a stub for a method call with this specific parameter value. So, if your tests try to make a POST for example or hit a different endpoint, you'll get an NPE again.
Even if you only ever use one request, it is better to define the stub for any argument, to avoid weird NPE failures if the code happens to send a different one (you are writing a test, so should not just assume automatically, that the code will always do what you expect - you would not need the test in the first place if that was the case):
when(test2.sendRequest(any)),thenReturn(Future.successful(...))
(Note Future.successful above – that's the correct way to create Future that is immediately satisfied, what you are doing makes it run on a thread ... not a big deal in your case, but still icky).
Then, after the test code is run, you can check that the parameter value passed to the sendRequest was actually what you expect:
verify(test2)
.sendRequest(HttpRequest(method = HttpMethods.GET, uri = "http://database:9000"))
Iam trying to get data from a web weather API, I'am getting the data by using WSClient.
Actually, I can println and visualize the data like this :
val futureResponse: Future[WSResponse] = complexRequest.get()
def weather = Action {
futureResponse.map {
response =>
println(response.json)
}
println(futureResponse)
Ok(views.html.weather("data"))
}
but I have trouble passing it to the view layer using Ok(views.html.weather("data")) cause when i println(futureResponse) its not json data it shows :
Future(Success(AhcWSResponse(StandaloneAhcWSResponse(200, OK))))
only println(response.json) shows the valid data i want to send but its unreachable outside.
You need something on the lines of
def weather = Action.async {
complexRequest.get().map(response => Ok(views.html.weather(response.json)))
}
So basically, the json is only available when the future is completed, so you can only pass it to the view inside the map function, also notice that I've used Action.async this creates an action that expects a Future[WsResponse] rather than just a WsResponse
Also bear in mind that the Futures are memoised, so if you store the reference to it in a val it will only execute once
EDIT: Fixed the future being stored in a val to avoid problems with the memoisation
It's unreachable because you will have to use a callback method to access/pass the content inside Future. That's the reason println(response.json) inside map callback shows the data/content you are interested.
You may refer to Accessing value returned by scala futures
I am trying to use the current version of slick and slick-codegen (3.2.0) with a sqlite database. When I try listing the table, I get the names properly. However, when I try to generate classes corresponding to the tables, I do not get any output.
This works:
object TableCodeGenerator extends App
{
val db = Database.forURL("jdbc:sqlite:/home/samik/db/mydb.db", driver = "org.sqlite.JDBC")
val tables = Await.result(db.run(MTable.getTables), 1 second).toList
tables.foreach(println)
}
I get the output below:
MTable(MQName(models),TABLE,null,None,None,None)
MTable(MQName(users),TABLE,null,None,None,None)
However, the following code, run directly in the same way, doesn't work:
object TableCodeGenerator extends App
{
val db = Database.forURL("jdbc:sqlite:/home/samik/db/mydb.db", driver = "org.sqlite.JDBC")
val dbio = SQLiteProfile.createModel(Some(MTable.getTables))
val model = db.run(dbio)
val codegenFuture: Future[SourceCodeGenerator] = model.map(model => new SourceCodeGenerator(model))
codegenFuture.onSuccess
{
case codegen => codegen.writeToFile(
"org.sqlite.JDBC",
"/tmp",
"my.package.dao",
"Tables",
"Tables.scala")
}
}
Meaning, the code runs successfully, but I do not see any output file. Is there anything I am missing?
The above was happening because the underlying code was silently throwing an exception. The reason for this exception was that I was using a "feature" of sqlite where if you don't mention the datatype in schema, sqlite assumes it to be text type. However that creates a problem for the slick code.
More details here. The immediate solution was to fix the schema, but I think this has now been fixed in slick as well.
My code looks like:
case class SRecord(trialId: String, private var _max:Int) {
def max=_max
def max_=(value:Int):Unit=_max=value
}
Then later on I apply a function onto it:
def groupSummaryRecords(it:Iterator[Option[SRecord]], optionSummary:Option[SRecord]):Option[SRecord] = {
var max=0;
var sRecord1 : Option[SRecord] = None
var i=0
while(it.hasNext) {
var sRecord:Option[SRecord] = it.next();
if(i==0) {
sRecord1 = sRecord;
}
..
}
sRecord1.max=max; // getting 'reassignment to val' compilation error
..
}
Why am i getting this compilation error, and how to fix it ?
If I instead change sRecord and sRecord1 instances to be of type SRecord instead of Option[SRecord] as well as the method signature, it all works fine however.
But in some cases I may have a null SRecord hence the use of None/Some. I am new to Scala, using Option/Some all over feels like a real pain if you ask me, i am just thinking of removing all this Option nonsense and testing for 'null' in good ol' Java, at least my code would work ??!
With the line sRecord1.max=max you are trying to call the max method on an Option[SRecord], not an SRecord. You want to access the contained SRecord (if any) and call the method on that, which can be done using foreach:
sRecord1.foreach(_.max=max)
which is desugared to:
sRecord1.foreach( srec => srec.max=max )
(the actual name "srec" is made up, the compiler will assign some internal name, but you get the idea). If sRecord1 is None, this won't do anything, but if it is Some(srec), the method execution will be passed in to operate on the contained instance.
Running a Play! app with Scala. I'm doing a request where the response is expected to be a JSON string. When checking the debugger, the JsonElement returns OK with all information as expected. However, the problem is when I try to actually run methods on that JsonElement.
val json = WS.url("http://maps.googleapis.com/maps/api/geocode/json?callback=?&sensor=true&address=%s", startAddress+","+startCity+","+startProvince).get.getJson
val geocoder = json.getAsString
The only error I get back is Unsupported Operation Exception: null and I've tried this on getAsString and getAsJsonObject and getAsJsonPrimitive
Any idea why it's failing on all methods? Thanks.
I had a similar problem and I had to change jsonObject.getAsString() to jsonObject.toString();
Maybe your JsonElement is a JsonNull
What you could do is to first check that it isn't by using json.isJsonNull
Otherwise, try to get its String representation with json.toString
In my case I just needed to get the element as an empty string if it is null, so I wrote a function like this:
private String getNullAsEmptyString(JsonElement jsonElement) {
return jsonElement.isJsonNull() ? "" : jsonElement.getAsString();
}
So instead of
val geocoder = json.getAsString
You can just use this
val geocoder = getNullAsEmptyString(json);
It returns "" if the element is null and the actual string if it is not
To add to #Henry's answer. In the spirit of Kotlins "OrNull" Adding an extension function:
fun JsonElement.asStringOrNull(): String? {
return if (isJsonNull) null else asString
}
The class JsonElement will throw Unsupported Operation Exception for any getAs<Type> method, because it's an abstract class and makes sense that it is implemented in this way.
For some reason the class JsonObject, does not implement the getAs<Type> methods, so any call to one of these methods will throw an exception.
Calling the toString method on a JsonElement object, may solve your issue in certain circumstances, but isn't probably what you want because it returns the json representation as String (e.g. \"value\") in some cases.
I found out that also a JsonPrimitive class exists and it does implement the getAs<Type> methods. So probably the correct way to proceed is something like this:
String input = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
JsonParser parser = new JsonParser();
JsonElement jsonTree = parser.parse(input);
if(jsonTree != null && jsonTree.isJsonObject()) {
JsonObject jsonObject = jsonTree.getAsJsonObject();
value = jsonObject.get("key1").getAsJsonPrimitive().getAsString()
}
PS. I removed all the nullability mgmt part. If you are coding in Java you probably want to manage this in a better way.
see GitHub source code for JsonElement:
https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/JsonElement.java#L178