Currently I am working on a hobby project with that I want to learn a bit about kotlin.
I implemented an object that makes HTTP get requests and returns the Json object from the response.
What I'm struggeling with is the mocking of the response or the http framework in my tests.
I think if the framework would provide a class, I could manage the mocking. But as it only provides functions like khttp.get(), I'm a bit confused how to mock that.
Can someone help me, please? :)
Thanks!
The HTTPClient Class:
package dao.http.HTTPClient
import khttp.get
import org.json.JSONObject
import java.net.URLDecoder
class HTTPClient {
fun getClient(): HTTPClient {
return this
}
fun httpRequestGET(url: String): JSONObject {
val r = get(url)
return r.jsonObject
}
}
And the related test Class
import dao.http.HTTPClient
import io.mockk.every
import io.mockk.spyk
import org.hamcrest.MatcherAssert.assertThat
import org.json.JSONObject
import org.junit.jupiter.api.Test
import org.hamcrest.CoreMatchers.`is` as Is
import khttp.responses.GenericResponse
class HTTPClientTest {
#Test
fun testHTTPRequestGET() {
val http_get = spyk(khttp.get( "https://somepage.com/wp-json/tsapi/v1/user/ts/isregistered/12323"))
val httpClient = HTTPClient()
var expectedAnswer: JSONObject = JSONObject("""{"uid":"1","user":"user","is_registered":"true"}""")
every { http_get } returns GenericResponse()
var url = "https://somepage.com/wp-json/tsapi/v1/user/ts/isregistered/12323"
var actualAnswer = httpClient.httpRequestGET(url)
assertThat(actualAnswer.get("user"), Is(expectedAnswer.get("user")))
}
}
you can use it like this:
#Test
fun test() {
mockkStatic("khttp.KHttp")
verify { khttp.get(any()) }
verify(exactly = 1) { khttp.get(url = "http://google.com") }
}
Related
I am new to Kotlin and Java so bear with me but I just wrote a Kotlin test as follows:
package com.squareup.cash.transactiongraph.service.actions
import com.squareup.cash.transactiongraph.TransactionGraphTestingModule
import com.squareup.cash.transactiongraph.client.franklin.FakeFranklinClient
import com.squareup.cash.transactiongraph.dataloader.DataLoaderRegistryFactory
import com.squareup.cash.transactiongraph.graphql.GraphQLContextFactory
import com.squareup.cash.transactiongraph.graphql.TransactionGraphContextFactory
import com.squareup.cash.transactiongraph.service.TransactionGraphGraphqlModule
import com.squareup.graphql.dataloaders.FlowDataLoaderDispatcher
import kotlinx.coroutines.future.await
import kotlinx.coroutines.runBlocking
import misk.testing.MiskTest
import misk.testing.MiskTestModule
import okhttp3.Headers
import org.junit.jupiter.api.Test
import org.assertj.core.api.Assertions.assertThat
#MiskTest(startService = true)
class CashCustomerTransactionsQueryTest {
#MiskTestModule
private val module = TransactionGraphTestingModule()
#Test
fun `returns an array of CashTransactions`() = runBlocking<Unit> {
val query = """
{
cashCustomerTransactions(customerToken: "customerToken") {
id
reasonCode
createdAt
}
}
""".trimIndent()
val result = execute(query)
assertThat(result["errors"]).isNull()
assertThat(result["data"]).isEqualTo(
mapOf(
"cashCustomerTransactions" to arrayOf(
mapOf(
"createdAt" to "2019-03-20T18:26:18Z",
"id" to "TOKEN",
"reasonCode" to "CARD_PRESENT_PURCHASE"
)
)
)
)
}
private suspend fun execute(query: String): Map<String, Any> {
val franklinClient = FakeFranklinClient()
val dataLoaderRegistryFactory = DataLoaderRegistryFactory()
val flowDataLoaderDispatcher = FlowDataLoaderDispatcher(dataLoaderRegistryFactory)
return flowDataLoaderDispatcher.run { registry ->
val contextFactory: GraphQLContextFactory =
TransactionGraphContextFactory(franklinClient)
TransactionGraphGraphqlModule().graphQL().executeAsync {
it
.query(query)
.context(contextFactory.build(Headers.Builder().build(), registry))
}.await().toSpecification()
}
}
}
Upon running the test it fails with the following error: expected: "{"cashCustomerTransactions"=[{"createdAt"="2019-03-20T18:26:18Z", "id"="TOKEN", "reasonCode"="CARD_PRESENT_PURCHASE"}]} (SingletonMap#58303289)" but was: "{"cashCustomerTransactions"=[{"createdAt"="2019-03-20T18:26:18Z", "id"="TOKEN", "reasonCode"="CARD_PRESENT_PURCHASE"}]} (LinkedHashMap#c32f16d)"
The following responses appear to be identical with the exception that one is a SingletonMap and one is a LinkedHashMap. I do not understand why the types are different. What am I doing incorrectly? Can someone please point me in the right direction. Thank you
Change arrayOf to listOf and the problem will be solved.
I have a rest controller using spring webflux and reactor, I am writing unit test for the controller. Please find below the code snippets and help me to write the unit test method to test the .doOnError() block.
I have tried to throw an exception by using Mockito
doThrow(CriticalException.class)
.when(myService).myMethod(object);
This is my unit test:
StepVerifier.create(
Mono.just(
webTestClient.post()
.uri("/endpoint")
.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(requestJson)) //Set the body of the request to the given synchronous Object
//Returns:a Mono with the response
//Act
.exchange() //Perform the exchange
//Assert
.expectStatus().isOk()
.expectBody(Agreement.class)
.returnResult()
.getResponseBody()))
.expectNextMatches(agreementResponse -> {
assertNotNull(agreementResponse.getAgreementParticipant());
return true;
})
.expectComplete()
.verify();
This is my controller:
return Mono.fromCallable(() -> {
myService.myMethod(object);
return object;
}).log().subscribeOn(Schedulers.elastic())
.map(p -> ResponseEntity.ok(p))
.defaultIfEmpty(ResponseEntity.notFound().build())
.doOnError(e -> {
LOGGER.error(LOG_FORMAT, e.getMessage(), e.getStackTrace());
});
Mockito is not returning exception while myService.myMethod(object) is been called.
Please suggest proper way to write test for .defaultIfEmpty() and .doOnError() blocks.
Instead of throwing CriticalException.class while mocking your myService.myMethod(object) return an exception wrapped in a Mono
For eg :
Mockito.doReturn(Mono.error(Exception::new)).when(service).callableMethod();
Find the sample code snippet below
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
class Service {
public Mono<String> callableMethod() {
return Mono.just("1");
}
}
class Controller {
private Service service;
public Controller(Service service) {
this.service = service;
}
public Mono<String> endpoint() {
return service.callableMethod().doOnError(throwable -> {
System.out.println("throwable = " + throwable);
});
}
}
public class TestClass {
#Mock
private Service service = Mockito.mock(Service.class);
#Test
public void controllerTest() {
Mockito.doReturn(Mono.error(Exception::new)).when(service).callableMethod();
StepVerifier.create(new Controller(service).endpoint()).verifyError();
}
}
So i have repository, which provides the Observable to the client. Is there a way i can mock this repository, so i don't need to send location from my emulator or using the real device to gain some Location?
Here is how the interface looks like:
interface RxLocationRepository {
#SuppressLint("MissingPermission")
fun onLocationUpdate(): Observable<Location>
fun stopLocationUpdates()
}
In my client side i use this like this:
class LocationManager(
val rxLocationRepository: RxLocationRepository){
private fun appendGeoEvent(location: Location) {
val locationGeoEvent = LocationGeoEvent(
accuracy = location.accuracy.toDouble(),
latitude = location.latitude,
longitude = location.longitude,
timestampGeoEvent = location.time
)
processGeoEvent(locationGeoEvent)
}
compositeDisposable.add(rxLocationRepository.onLocationUpdate()
.subscribe(Consumer { location ->
appendGeoEvent(location)
}))
....
So i sending this obtained location to my appendGeoEvent method.
I can use for example Mockito, but i don't know how to mock this repository so i can use the fake locations.
Also, i want to use Kotlin.
If using Mockito, you could do something like this:
import android.location.Location
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.junit.MockitoJUnitRunner
import java.util.*
#RunWith(MockitoJUnitRunner::class)
class LocationsTest{
#Test
fun should_return_one_location() {
val location = Location("test").apply {
latitude = 1.234
longitude = 5.678
// ...
}
val mockRepository = mock(RxLocationRepository::class.java)
`when`(mockRepository.onLocationUpdate()).thenReturn(Observable.just(location))
// use the mock
}
}
I am using: testCompile "org.mockito:mockito-core:2.11.0"
I needed to setup different Dagger module, which just provide me implementation of this repository - it returns different Observable stream.
I set it up like this.
#Module
abstract class StubLocationRepositoryModule {
#Binds
internal abstract fun bindsRxLocationRepository(stubRxLocationRepository: StubRxLocationRepository) : RxLocationRepository
}
I was just using this in my androidTest package in component.
So my implementation was like this:
just an example:
class StubRxLocationRepository #Inject constructor(val stubLocationParameterName: String) : RxLocationRepository {
val location = Location("test").apply {
latitude = 1.234
longitude = 5.678
accuracy = 20f
time = Date().time
}
override fun onLocationUpdate(): Observable<Location> {
Log.v("StubLocationRepository", "CHOSEN PARAMETER: $stubLocationParameterName")
return when (stubLocationParameterName) {
"highFreq" -> Observable.interval(50, TimeUnit.MILLISECONDS)
.flatMap(
{
Observable.just(location)
}
)
.doOnNext{ t: Location -> Log.v("onLocationUpdate", t.toString()) }
else -> Observable.interval(1, TimeUnit.SECONDS)
.flatMap(
{
Observable.just(location)
}
)
.doOnNext{ t: Location -> Log.v("onLocationUpdate", t.toString()) }
}
}
override fun stopLocationUpdates() {
Log.v("StubLocationRepository", "stopLocationUpdates")
}
}
So in my Dagger Component builder i expose a method which will provide me some parameter in which i will depend in the StubRxLocation implementation - it will return me some stream i need for specific test case.
So the component:
#Singleton
#Component(modules = arrayOf(StubLocationRepositoryModule::class,
LocationTestInstrumentalModule::class, StubRssiRepositoryModule::class))
interface LocationTestInstrumentalComponent{
fun locationClient(): LocationClient
#Component.Builder
interface Builder {
#BindsInstance
fun context(context: Context): Builder
#BindsInstance
fun stubLocationRepositoryParameter(stubLocationRepositoryParameter: String): Builder
fun build(): LocationTestInstrumentalComponent
}
}
So in every test i can bring the mocked repository it like this, which will be ready form me to use for that test case:
#Test
fun someTest(){
val component = DaggerLocationTestInstrumentalComponent.builder().stubLocationRepositoryParameter("highFreq").context(InstrumentationRegistry.getContext()).build()
val client = component.locationClient()
//i can expose some other methods, not only this 'locationClient' in this Component to return me some classes, like this RxLocationRepository(which will behave as i want) and others
}
I'm trying to test a Dart REST app run on shelf_rest. Assuming a setup similar to the shelf_rest example, how can one test the configured routes without actually running an HTTP server?
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_rest/shelf_rest.dart';
void main() {
var myRouter = router()
..get('/accounts/{accountId}', (Request request) {
var account = new Account.build(accountId: getPathParameter(request, 'accountId'));
return new Response.ok(JSON.encode(account));
});
io.serve(myRouter.handler, 'localhost', 8080);
}
class Account {
final String accountId;
Account.build({this.accountId});
Account.fromJson(Map json) : this.accountId = json['accountId'];
Map toJson() => {'accountId': accountId};
}
class AccountResource {
#Get('{accountId}')
Account find(String accountId) => new Account.build(accountId: accountId);
}
Without getting into too much additional logic, how could the GET account endpoint be unit tested? Some basic tests I'd like to run would be:
GET /accounts/123 returns 200
GET /accounts/bogus returns 404
To create a unit test (i.e. without a running server) then you need to split myRouter outside of the main function and put it in a file in the lib dir. e.g
import 'dart:convert';
import 'package:shelf/shelf.dart';
import 'package:shelf_rest/shelf_rest.dart';
var myRouter = router()
..get('/accounts/{accountId}', (Request request) {
var account =
new Account.build(accountId: getPathParameter(request, 'accountId'));
return new Response.ok(JSON.encode(account));
});
class Account {
final String accountId;
Account.build({this.accountId});
Account.fromJson(Map json) : this.accountId = json['accountId'];
Map toJson() => {'accountId': accountId};
}
Then create a test file in the test directory and test like
import 'package:soQshelf_rest/my_router.dart';
import 'package:test/test.dart';
import 'package:shelf/shelf.dart';
import 'dart:convert';
main() {
test('/account/{accountId} should return expected response', () async {
final Handler handler = myRouter.handler;
final Response response = await handler(
new Request('GET', Uri.parse('http://localhost:9999/accounts/123')));
expect(response.statusCode, equals(200));
expect(JSON.decode(await response.readAsString()),
equals({"accountId": "123"}));
});
}
I have a controller that takes in a file as part of the parameter. I'm wondering how can I test this?
my controller action:
def save () {
def colorInstance = new Color(params.color)
CommonsMultipartFile file = request.getFile('color.filename')
fileUploadService.upload(colorInstance, file, "foldername")
if (requestInstance.save(flush: true)) {
withFormat {
html {redirect(action: "list") }
js {render "test"}
}
}
}
I've started with something like this:...
import org.junit.Before
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
#TestFor(ColorController)
#Mock(Color)
class ColorControllerTests {
#Before
void setUp() {
controller.fileUploadService = new FileUploadService
}
}
Question
I can't figure out how to test the CommonsMultipartFile for file upload.
Also, this test case will sit in unit test folder. How can I execute it?
I am using Grails 2.4, and you can simply use GrailsMockMultipartFile and request.addFile method in your unit test.
This code works on Grails 2.4.4, with Spock testing framework:
Controller side:
class FileUploadController {
def upload() {
def multipartFile = request.getFile('requestParamName')
if (multipartFile.empty) {
flash.message = 'Error: file cannot be empty'
render(view: 'upload')
return
}
// do something now with your file
}
}
Unit test side:
import grails.test.mixin.TestFor
import org.codehaus.groovy.grails.plugins.testing.GrailsMockMultipartFile
import spock.lang.Specification
#TestFor(FileUploadController)
class FileUploadControllerSpec extends Specification {
void "upload should add flash error message if empty file in request"() {
given:
def multipartFile = new GrailsMockMultipartFile('requestParamName', 'someExcelFile.xls', 'application/vnd.ms-excel', new byte[0])
request.addFile(multipartFile)
when:
controller.upload()
then:
assertEquals('Error: file cannot be empty', flash.message)
}
}
Since the request will be multipart during file upload, the actual request servlet would be MultipartHttpServletRequest. For unit test case, mock the same and use it as the request in controller. On successful mocking you should be able to addFile in tests and getFile in action.
import org.junit.Before
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
#TestFor(ColorController)
#Mock(Color)
class ColorControllerTests {
#Before
void setUp() {
controller.fileUploadService = new FileUploadService
//Mock MultipartHttpServletRequest somethign like below
controller.metaClass.request = mockFor(MultipartHttpServletRequest).createMock()
}
void testFileUpload(){
//Add a mock multipart file to request
controller.request.addFile(new MockMultipartFile('myFile', 'IronMan3.jpg', 'image/jpeg', "1234567" as byte[]))
//call the controller action
//assert response
}
}
The accepted answer did not work for me. The following code did:
import org.junit.Before
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import org.springframework.mock.web.MockMultipartHttpServletRequest
import org.springframework.mock.web.MockMultipartFile
#TestFor(ColorController)
class ColorControllerTests {
def mockRequest
#Before
void setUp() {
mockRequest = new MockMultipartHttpServletRequest()
controller.metaClass.request = mockRequest
// your service here
controller.fileUploadService = new FileUploadService
}
void testFileUpload(){
//Add a mock multipart file to request
mockRequest.addFile(new MockMultipartFile('myFile', 'IronMan3.jpg', 'image/jpeg', "1234567" as byte[]))
//call the controller action
//assert response
}
}