Having trouble writing unit test for Kotlin Object - unit-testing

With the logic of TDD in mind and trying to understand how to write unit test's, I am having trouble with Kotlin object. The test pass but I am unsure if this is actually the right test. I am trying to make sure that the Logger.i() method is called and that it saves into the database. But at the moment I am stuck at just its called part.
My Object
object Logger {
fun i(tag: String, msg: String, tr: Throwable? = null): Int {
insertIntoLogDatabase(createLogModel("i", tag, msg, tr))
return if (BuildConfig.DEBUG) Log.i(tag, msg, tr) else 0
}
private fun insertIntoLogDatabase(log: LogModel) {
//Insert into Log DB
logRepo.upsert(log)
}
private fun createLogModel(type: String, tag: String, msg: String, tr: Throwable?) = LogModel(0, type, tag, msg, if (tr != null) tr.message + "\n" + tr?.stackTrace.contentToString() else null)
fun setLogRepo(logRepo: LogRepository) {
this.logRepo = logRepo
}
}
with this, I know that I have to call Logger.setLogRepo(logRemp) to give the Logger access to the repo (and this works)
Where I am stuck is I am trying to unit test the Log.i method call
I have this
#Mock
lateinit var log: Logger
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
Logger.setLogRepository(logRepo)
}
#Test
fun `log i failed`() {
// When
log.i("Test", "Test1")
// Then
verify(log, times(1)).i("Test", "Test1")
}
I mean this works but is it correct (my gut tells me that something is wrong that I am not actually testing the Logger.i() method
please advise.
Thanks.

As you mentioned, you have to test that when you log the data, it should get stored in the repo in the format you want, and you want to test basically that when you have given the work of sending log to the Logger, it is getting sent to the repository in the correct format and you will assert against that.
So you're test case will look like,
#Mock
lateinit var logRepo: LogRepository
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
Logger.setLogRepository(logRepo)
}
#Test
fun `when an item is logged, it gets stored in the repository`()
{
val expectedData= <valueThatShouldBeSentToRepo>
Logger.i("Test", "Test1")
verify(logRepo).upsert(expectedData)
}

Related

How to write unit test for spring cloud stream function based method?

When I try to test a spring cloud stream function based method, it always happens NullPointerException about InputDestination.
I have two questions:
It's hard for me to know how to write UT from the official doc. official test doc
Besides, how to write integration Test if test file has some dependencies. It seems create a new context and always has NoSuchBeanDefination error.
I have tried as flow, but the context can not find some dependency beans.
#Test
public void sampleTest() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
TestChannelBinderConfiguration.getCompleteConfiguration(
MyTestConfiguration.class))
.run("--spring.cloud.function.definition=uppercase")) {
InputDestination source = context.getBean(InputDestination.class);
OutputDestination target = context.getBean(OutputDestination.class);
source.send(new GenericMessage<byte[]>("hello".getBytes()));
assertThat(target.receive().getPayload()).isEqualTo("HELLO".getBytes());
}
}
So I just want to write UT, but still have NPE.
Here is my code.
#Bean
public Function<Message<List<DemoBean>>, Message<DemoBean>> findFirstBean( ){
return message -> {
List<DemoBean> demoBeans = message.getPayload();
return MessageBuilder.withPayload(demoBeans.get( 0 )).build();
};
}
Here is my test.
#SpringBootTest
#ActiveProfiles(profiles = "local")
#Import({ TestChannelBinderConfiguration.class})
class FunctionDemoTest {
#Autowired
private InputDestination inputDestination;
#Autowired
private OutputDestination outputDestination;
private FunctionDemo functionDemo;
// some dependency need to mock
private DemoService demoService;
#BeforeEach
void setUp() {
demoService = Mockito.mock( DemoService.class );
functionDemo = new FunctionDemo( demoService);
}
#Test
public void findFirstBeanTest() {
DemoBean demoBean = new DemoBean();
demoBean.setName("Howard");
demoBean.setAge( 1 );
DemoBean demoBean1 = new DemoBean();
demoBean1.setName("Frank");
demoBean1.setAge( 2 );
List<DemoBean> demoBeanList = new ArrayList<>();
demoBeanList.add( demoBean );
demoBeanList.add( demoBean1 );
Message<List<DemoBean>> inputMessage = MessageBuilder.withPayload(demoBeanList).build();
inputDestination.send(inputMessage,"findFirstBean-in-0");
Assertions.assertNotNull( outputDestination.receive( 10000, "findFirstBean-out-0") );
}
}
Here is error:
java.lang.NullPointerException: while trying to invoke the method org.springframework.messaging.SubscribableChannel.send(org.springframework.messaging.Message) of a null object returned from org.springframework.cloud.stream.binder.test.InputDestination.getChannelByName(java.lang.String)
at org.springframework.cloud.stream.binder.test.InputDestination.send(InputDestination.java:89)
at com.successfactors.caf.listener.FunctionDemoTest.raePdrResultProcessor(FunctionDemoTest.java:82)
Well, I know the root cause of NPE.
Message<byte[]> receive(long timeout, String bindingName)
It seems should be destinationName instead of bindingName in source code.
Any other answers would be appreciated.

Unit test with mockK - flatMap

I'm new in Unit Testing, I can't understand how to test this kind of method with kotlin, using MockK:
override fun register(firebaseId: String, uniqueId: String): Completable {
return Observable.just(0).observeOn(schedulerProvider.io()).flatMap {
val requestRegisterPushes = registerBuilder.build(firebaseId, uniqueId)
apiServiceFactory.build()
.registerPushes(requestRegisterPushes)
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
}.flatMapCompletable {
Completable.complete()
}
}
This is my code for the test, the test was a success but the condition coverage does not increase.
#Test
fun `register_Test()`() {
val requestRegisterPushes = mockk<RequestRegisterPushes>(relaxed = true)
every { registerBuilder.build(any(), any(), any(), any()) } returns requestRegisterPushes
every { apiServiceFactory.build().register(requestRegisterPushes) } returns Observable.just(SimpleResponse())
val resp = userRepository.register("x7gbyb68837g78s", "XXX-XXX-XXX")
}
I would really appreciate it if you could help me a little.
Of course you don't have any coverage because code inside flatmap and flatMapCompletable operator was not executed.
You need to subscribe to Observable to make it emmit elements in you case it will emmit 0 only when you subscribe for it. That's how RxJava works. Something like this:
val subscriber = TestSubscriber<>();
val resp = userRepository.register("x7gbyb68837g78s", "XXX-XXX-XXX").subscribe(subscriber)
subscriber.assertComplete()

How to unit test ConflatedBroadcastChannel?

I have a test here, where I am attempting to validate what is being sent to a ConflatedBroadcastChannel (experimental API, I know):
#Test
fun myTest() = runBlockingTest {
val results = ArrayList<String>()
val myChannel = ConflatedBroadcastChannel<String>()
myChannel.openSubscription().consumeEach {
results.add(it)
}
myChannel.send("hello")
assertEquals(1, results.size)
}
But when I run that code, I receive an IllegalStateException with the messge: "This job has not completed yet". Anyone know what's missing? I've tried using TestCoroutineDispatcher as well to no avail.
If you just want to obtain a single value from a channel you can always use receive.
Since ConflatedBroadcastChannel caches the latest value, you can just use it like this:
#Test
fun myTest() = runBlockingTest {
val myChannel = ConflatedBroadcastChannel<String>()
myChannel.send("hello")
val subscription = myChannel.openSubscription()
val result = subscription.receive()
subscription.cancel()
assertEquals("hello", result)
}
So I thought about the message I was getting, and suspected maybe that the "job that's not completed yet" was the .consumeEach operation I was performing. So to be explicit, I wrapped the operation in a launch(myTestCoroutineDispatcher) and called cancel. This doesn't seem like the ideal way to test things, but it makes sense given that the test environment isn't really guessing when your channel should stop receiving things. Anyways, lo and behold, this works:
#Test
fun myTest() = runBlockingTest {
val results = ArrayList<String>()
val myChannel = ConflatedBroadcastChannel<String>()
launch(testMainDispatcher) {
myChannel.openSubscription().consumeEach {
results.add(it)
cancel()
}
}
myChannel.send("hello")
assertEquals(1, results.size)
}
EDIT: another thing you can do to achieve same results is val myJob = launch {...} and then at the end of your test execution, call myJob.cancel() for the same effect.

How can I use Mockito to test this function?

I'm trying to test that Permission.REVEAL_NOW returns the method isFeatureRevealNowAvailable.
Heres some code I already tried but didn't succeed with. Any help would be hugely appreciated as always!
Function to test:
class PermissionRepository(private val permissionApi: PermissionApi,
private val appPreferences: AppPreferences) {
fun checkPermission(permission: PermissionType, onPermissionResponse: (Boolean) -> Unit) {
Log.i("Permission", "Checking permission")
when (permission) {
PermissionType.REVEAL_NOW -> {
isFeatureRevealNowAvailable(onPermissionResponse, ::errorHandler)
}
}
}
Attempted solution:
#RunWith(MockitoJUnitRunner::class)
class PermissionRepositoryTest{
#Test
fun checkPermissionTest() {
val mockPermissionRepository = mock(PermissionRepository::class.java)
val mockPermissionApi = mock(PermissionApi::class.java)
val result = mockPermissionRepository.checkPermission(PermissionType.REVEAL_NOW, onPermissionResponse = null)
//Unsure of what to use here AssertThat or Mockito's "when" function
}
}
private fun isFeatureRevealNowAvailable(permissionResponseHandler: (Boolean) -> Unit, permissionError: (Throwable) -> Unit) {
permissionApi.getRevealNowPermission().enqueue(object : Callback<PermissionResponse> {
override fun onFailure(call: Call<PermissionResponse>, t: Throwable) {
permissionResponseHandler(false)
permissionError(t)
}
override fun onResponse(call: Call<PermissionResponse>, response: Response<PermissionResponse>) {
val permissionResult = response.body()?.isRevealNow ?: false
updateUserLocalPermission(PermissionType.REVEAL_NOW, permissionResult)
permissionResponseHandler(permissionResult)
}
})
}
(TL;DR - go to Example)
Since you don't define specifically what you want to test or achieve, I will give some overall tips:
Mocking
Never mock the class you want to test
val mockPermissionRepository = mock(PermissionRepository::class.java)
Mock only what you want to exclude from your test but rely on
Try to avoid mocks whenever it is possible and makes sence, because they simulate a perfect world, not the real behaviour
Test naming
Use proper names to describe what you want to achive with your test. My personal favourite is to start the sentence with the word "should ..."
Example
Two tests I could image to write for your function:
#Test
fun `should invoke reveal permission on PermissionApi when type is REVEAL_NOW`(){
val mockPermissionApi = mock(PermissionApi::class.java)
val permissionRepository = PermissionRepository(mockPermissionApi, mock())
permissionRepository.checkPermission(PermissionType.REVEAL_NOW, onPermissionResponse = {})
verify(mockPermissionApi, times(1)).getRevealNowPermission()
}
#Test
fun `should do nothing when type is not REVEAL_NOW`() {
val mockPermissionApi = mock(PermissionApi::class.java)
val permissionRepository = PermissionRepository(mockPermissionApi, mock())
permissionRepository.checkPermission(PermissionType.ELSE, onPermissionResponse = {})
verify(mockPermissionApi, times(0)).getRevealNowPermission()
}

Junit error - No value present or no such element

testclass.java
#Test
public void testgetDictionaryValueListById() {
DictionaryValue dictionaryValue = new DictionaryValue();
dictionaryValue.setId(1);
dictionaryValue.setValueName("Test Dictionary Value");
dictionaryValue.setValueKey("12345678");
dictionaryValue.setStatus("Active");
dictionaryValue.setCreatedOn(new Date());
dictionaryValue.setUpdatedOn(new Date());
Mockito.when(dictionaryValueRepo.findById(1).get()).thenReturn(dictionaryValue);
assertThat(dictionaryService.getDictionaryValueListById(1)).isEqualTo(dictionaryValue);
}
Service.java
public DictionaryValue getDictionaryValueListById(int id) {
return dictionaryValueRepo.findById(id).get();
}
Repo.java
#Repository
public interface DictionaryValueRepo extends JpaRepository<DictionaryValue, Integer> {
}
I am getting no such value present again and again on executing test case in testclass.java. I don't know why? but when I am running my service method from the controller it is working as expected - fetching records from the database but not working in a test case.
Your test should be like this and please check out the naming. You need to Mock the step findId() befor the `get().
#InjectMocks
Service cut;
#Mock
DictionaryValueRepo dictionaryValueRepoMock;
// Can skipped by adding a #RunWith... on Testclass
#Before
public init() {
Mockito.initMocks(this);
}
#Test
public void testgetDictionaryValueListById() {
// Prepare Data
final int testId = 1;
DictionaryValue dictionaryValue = new DictionaryValue();
dictionaryValue.setId(testId);
dictionaryValue.setValueName("Test Dictionary Value");
dictionaryValue.setValueKey("12345678");
dictionaryValue.setStatus("Active");
dictionaryValue.setCreatedOn(new Date());
dictionaryValue.setUpdatedOn(new Date());
// config mocking
Mockito.when(dictionaryValueRepo.findById(testId)).thenReturn(<VALUE>);
Mockito.when(dictionaryValueRepo.findById(testId).get()).thenReturn(dictionaryValue);
// Call yout method for Testing
cut.getDictionaryValueListById(testId);
// verifies (if wanted) + assertions....
}
I concur with LenglBoy, so the right answer should be given to him.
The thing you need to be careful is what "VALUE" means in this line:
Mockito.when(dictionaryValueRepo.findById(testId)).thenReturn(VALUE);
The findById returns an Optional, so that is what you should build and pass to Mockito. Something like this:
Mockito.when(dictionaryValueRepo.findById(testId))
.thenReturn(Optional.ofNullable(dictionaryValue));
And for a scenario where the id does not exists in BD, passing Optional.empty() should be good enough.