I am trying to test room database insertion and fetching with the help of TestCoroutineDispatchers and runBlockingTest. As I need the context for creating a database instance, I tried to achieve the test in androidTest, but I am getting below error. Can any one please help me out with a solution.
I am following the instructions written at this link as follows https://medium.com/#eyalg/testing-androidx-room-kotlin-coroutines-2d1faa3e674f
"Unresolved reference: TestCoroutineDispatcher"
"Unresolved reference: TestCoroutineScope"
"Unresolved reference: runBlockingTest"
#ExperimentalCoroutinesApi
#RunWith(AndroidJUnit4::class)
class PokemonDatabaseTest {
private lateinit var pokemonDao : PokemonFavouriteDao
private lateinit var db : PokemonDatabase
val testDispatcher = TestCoroutineDispatcher()
val testScope = TestCoroutineScope(testDispatcher)
#Before
fun setUp() {
db = Room
.inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().context, PokemonDatabase::class.java)
.setTransactionExecutor(testDispatcher.asExecutor())
.setQueryExecutor(testDispatcher.asExecutor()).build()
pokemonDao = db.pfDao
}
#Test
fun storeFavouritePokemon() = runBlockingTest {
val pokemon = DataFactory.makePokemon()
assertThat(pokemonDao.getFavouritePokemon(), null)
}
}
And I am using below dependencies :
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.0'
testImplementation 'androidx.test:core:1.2.0'
testImplementation "org.robolectric:robolectric:4.0.2"
def archCoreVersion = '2.1.0'
def espressoCoreVersion = "3.3.0"
def espressoVersion = '3.3.0'
espressoCore: "androidx.test.espresso:espresso-core:$espressoCoreVersion",
espressoContrib : "androidx.test.espresso:espresso-contrib:$espressoVersion",
espressoIntents : "androidx.test.espresso:espresso-intents:$espressoVersion",
espressoResource: "androidx.test.espresso:espresso-idling-resource:$espressoVersion",
coreRuntime : "androidx.arch.core:core-runtime:$archCoreVersion",
coreTesting : "androidx.arch.core:core-testing:$archCoreVersion"
You declared testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2' for your unit tests.
But if you want to use TestCoroutineDispatcher for androidTest you have to declare it's dependency as well: androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'.
Same for other dependencies you wanna use for androidTest's.
Related
I have the following classes
interface CarsApi {
suspend fun fetchCar() : Car
}
class FetchCarUseCase(private val carsApi: CarsApi) {
suspend fun execute: Car = withContext(dispatcherProvider.io()) {
carsApi.fetchCar()
}
}
class ViewModel(private val fetchCarUseCase: FetchCarUseCase) {
private var car: Car
suspend fun retrieveCar() {
car = fetchCarUseCase.execute()
}
}
I want to write an ermetic test for the viewModel and the useCase:
#Test
fun testCarFetching() = runBlockingTest {
val aCar = Car()
val mockApi = mock<CarsApi>()
`when`(mockApi.fetchCar()).thenReturn(aCar)
val fetchCarUseCase = FetchCarUseCase(mockApi)
val viewModel = ViewModel(fetchCarUseCase)
viewModel.retrieveCar()
/* assert stuff on viewModel.car*/
}
But the viewModel.car always seems to be null. Inside the test body mockApi.fetchCar() does retrieve the provided value, but inside the FetchCarUseCase it does not. Also if I remove the suspend keyword from the interface, the mocking seems to be working fine.
At the moment, due to some other conditions I cannot use Mockk library, so I'm stuck with Mockito.
Am I missing something?
The used dependencies:
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.28.2'
testImplementation('com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0') {
exclude module: 'mockito-core'
}
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2
In case anyone else has to deal with this problem, here is the infrastructure I have build.
First, in all the classes that launch threads inject through the constructor or property a kotlinx.coroutines.DispatcherProvider. In my case it was just the useCase, but the viewModel might require it, as well.
class FetchCarUseCase(private val dispatcher: CoroutineDispatcher,
private val carsApi: CarsApi) {
suspend fun execute: Car = withContext(dispatcher) {
carsApi.fetchCar()
}
}
In the unit tests project, add a helper rule-class, in order to extract some functionality:
#ExperimentalCoroutinesApi
class CoroutineTestRule(val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()) : TestWatcher() {
val testDispatcherProvider = object : DispatcherProvider {
override fun default(): CoroutineDispatcher = testDispatcher
override fun io(): CoroutineDispatcher = testDispatcher
override fun main(): CoroutineDispatcher = testDispatcher
override fun unconfined(): CoroutineDispatcher = testDispatcher
}
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
}
And finally the unit test looks like this:
#ExperimentalCoroutinesApi
#RunWith(MockitoJUnitRunner::class)
class ViewModelTest {
#get:Rule
var coroutinesTestRule = CoroutineTestRule()
#Test
fun testCarFetching() = coroutinesTestRule.testDispatcher.runBlockingTest {
val aCar = Car()
val mockApi = mock<CarsApi>()
`when`(mockApi.fetchCar()).thenReturn(aCar)
val fetchCarUseCase = FetchCarUseCase(mockApi)
val viewModel = ViewModel(fetchCarUseCase)
viewModel.retrieveCar()
/* assert stuff on viewModel.car*/
}
#Test
fun testCarFetchingError() = coroutinesTestRule.testDispatcher.runBlockingTest {
val aCar = Car()
val mockApi = mock<CarsApi>()
`when`(mockApi.fetchCar()).then {
throw Exception()
}
val fetchCarUseCase = FetchCarUseCase(mockApi)
val viewModel = ViewModel(fetchCarUseCase)
viewModel.retrieveCar()
/* assert stuff on erros*/
}
}
This way all the code in the unit tests runs on the same thread and in the same context.
I am using EF Core in a projet to get stored procedure calling. In my context i have the following :
public class MyContext : DbContext
{
public DbQuery<User> UserQuery { get; set; }
public MyContext(DbContextOptions<MyContext> options) : base(options) { }
}
And i call the stored procedure like this :
public virtual async Task<User> GetUserAsync(string name)
{
return await MyContext.Query<User>()
.FromSql($"EXEC [dbo].[GetUser], #Login = {name}")
.FirstOrDefaultAsync();
}
Code is working fine. I need to test this method in unit tests, i'm using InMemoryDatabase to mock my context MyContext like this :
[Fact]
public async Task GetUserAsync_should_return_first_user_with_login_and_password_if_exists()
{
// Arrange
var users = new List<User>
{
new User()
{
Login = "test#outlook.fr",
Password = "pass1",
},
};
var options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase(databaseName: "BddName")
.Options;
var context = new MyContext(options);
var loginProvider = A.Fake<LoginProvider>(opts => opts.WithArgumentsForConstructor(() => new LoginProvider(context)));
// Act
// Assert
context.Dispose();
}
And i have no idea how can i set my list into the result of the stored procedure called from DbQuery. I tried to follow this article : https://nodogmablog.bryanhogan.net/2017/11/unit-testing-entity-framework-core-stored-procedures/ but it works for DbSet only and not DbQuery.
I need some advices for this case.
Thanks in advance.
The link in the OP does apply to the DbQuery type as well, as you're mocking the provider. Both DbSet and DbQuery work in the same way in this regard.
See https://stackoverflow.com/a/56940311/2975810 for a previous answer on the topic.
Hi i am getting the following error
org.koin.error.BeanInstanceCreationException: Can't create definition for 'Single [name='NetworkControllerContract',class='com.network.contract.NetworkControllerContract']' due to error :
Mockito cannot mock this class: class com.network.NetworkController.
I have manually made those classes open..
open class NetworkController constructor(private val networkHelper: NetworkHelperContract) : NetworkControllerContract{.....}
open interface NetworkControllerContract {
}
//my test class
#RunWith(AndroidJUnit4::class)
class MyUnitTest: KoinTest {
single<NetworkControllerContract> {
Mockito.mock(NetworkController::class.java) //where it crashes
}
val networkController: NetworkControllerContract by inject()
}
Dependencies i use
def mockito = "2.21.0"
//android instrumental test
androidTestImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.ext:truth:1.1.0'
androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation('androidx.arch.core:core-testing:2.0.0') {
exclude group: 'org.mockito:mockito-core'
}
androidTestUtil 'androidx.test:orchestrator:1.1.1'
androidTestImplementation 'com.github.tmurakami:dexopener:2.0.0'
androidTestImplementation 'com.jraska.livedata:testing-ktx:0.6.0'
androidTestImplementation 'com.jraska.livedata:testing:0.6.0'
androidTestImplementation "org.mockito:mockito-core:$mockito"
androidTestImplementation("org.mockito:mockito-android:$mockito") {
exclude group: 'org.mockito'
}
androidTestImplementation('org.koin:koin-test:1.0.2') {
exclude group: 'org.mockito'
}
testImplementation 'junit:junit:4.12'
testImplementation "org.mockito:mockito-core:$mockito"
testImplementation "org.mockito:mockito-android:$mockito"
testImplementation "org.mockito:mockito-inline:$mockito"
testImplementation 'org.koin:koin-test:1.0.2'
testImplementation 'androidx.test.ext:junit:1.1.0'
testImplementation 'androidx.arch.core:core-testing:2.0.0'
testImplementation 'com.jraska.livedata:testing-ktx:0.6.0'
testImplementation 'com.jraska.livedata:testing:0.6.0'
testImplementation 'androidx.test.ext:truth:1.1.0'
It is my decision for mocking
#RunWith(AndroidJUnit4::class)
class DashboardFragmentTest : KoinTest {
#Rule
#JvmField
val activityRule = ActivityTestRule(SingleFragmentActivity::class.java, true, true)
#Rule
#JvmField
val executorRule = TaskExecutorWithIdlingResourceRule()
#Rule
#JvmField
val countingAppExecutors = CountingAppExecutorsRule()
private val testFragment = DashboardFragment()
private lateinit var dashboardViewModel: DashboardViewModel
private lateinit var router: Router
private val devicesSuccess = MutableLiveData<List<Device>>()
private val devicesFailure = MutableLiveData<String>()
#Before
fun setUp() {
dashboardViewModel = Mockito.mock(DashboardViewModel::class.java)
Mockito.`when`(dashboardViewModel.devicesSuccess).thenReturn(devicesSuccess)
Mockito.`when`(dashboardViewModel.devicesFailure).thenReturn(devicesFailure)
Mockito.`when`(dashboardViewModel.getDevices()).thenAnswer { _ -> Any() }
router = Mockito.mock(Router::class.java)
Mockito.`when`(router.loginActivity(activityRule.activity)).thenAnswer { _ -> Any() }
StandAloneContext.loadKoinModules(hsApp + hsViewModel + api + listOf(module {
single(override = true) { router }
factory(override = true) { dashboardViewModel } bind ViewModel::class
}))
activityRule.activity.setFragment(testFragment)
EspressoTestUtil.disableProgressBarAnimations(activityRule)
}
#After
fun tearDown() {
activityRule.finishActivity()
StandAloneContext.closeKoin()
}
#Test
fun devicesCall() {
onView(withId(R.id.rv_devices)).check(ViewAssertions.matches(ViewMatchers.isCompletelyDisplayed()))
Mockito.verify(dashboardViewModel, Mockito.times(1)).getDevices()
}
}
Problem is in your setup:
testImplementation "org.mockito:mockito-android:$mockito"
This dependency uses Android internals, which aren't available when you run Unit tests.
I am trying to implement junit testing with spring data jpa application. On controller level I am trying to implement unit testing. But I am getting Test failure class cast exception error.
DepartmentController.java
#RestController
#RequestMapping("/api.spacestudy.com/SpaceStudy/Control/SearchFilter")
public class DepartmentController {
#Autowired
DepartmentService depService;
#CrossOrigin(origins = "*")
#GetMapping("/loadDepartments")
public ResponseEntity<Set<Department>> findDepName() {
Set<Department> depname = depService.findDepName();
return ResponseEntity.ok(depname);
}
}
Junit test class
#RunWith(SpringRunner.class)
#WebMvcTest(DepartmentController.class)
public class SpaceStudyControlSearchFilterApplicationTests {
#Autowired
DepartmentController depController;
#Autowired
private MockMvc mockMvc;
#MockBean
DepartmentService depService;
#SuppressWarnings("unchecked")
Set<Department> mockDepartment = (Set<Department>) new Department(21629, "170330", "Administrative Computer");
#Test
public void findDepNameTest() throws Exception {
Mockito.when(depService.findDepName()).thenReturn( mockDepartment);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/api.spacestudy.com/SpaceStudy/Control/SearchFilter/loadDepartments").accept(
MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println(result.getResponse());
String expected = "{nDeptId: 21629}";
JSONAssert.assertEquals(expected, result.getResponse().getContentAsString(), false);
}
}
Junit failure
java.lang.ClassCastException: com.spacestudy.model.Department cannot be cast to java.util.Set
at com.spacestudy.SpaceStudyControlSearchFilterApplicationTests.<init>(SpaceStudyControlSearchFilterApplicationTests.java:39)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
I am new to junit testing. Can any one tell me what I am doing wrong in test?
You are trying to cast a Department to Set<Department> at this line:
Set<Department> mockDepartment = (Set<Department>) new Department(21629, "170330", "Administrative Computer");
This cannot work. Instead you should create an empty set and then add the department, i.e. like this:
Set<Department> mockDepartment = new HashSet<Department>() {{
add(new Department(21629, "170330", "Administrative Computer"));
}};
I'm trying to implement unit test using Spock framework this is what my test looks like:
def setup() {
scrollableResultsMock = Mock(ScrollableResults)
paymentRepositoryMock = Mock(PaymentRepository)
paymentRegistryService = new PaymentRegistryService(paymentRepositoryMock)
}
#Unroll
def 'should correctly process'() {
given:
paymentRepositoryMock.findUnconfirmedTransactions(_ as LocalDate, _ as Days) >> scrollableResultsMock
...
}
Here is class in which I'm trying to inject mocked object:
#Service
open class PaymentRegistryService
#Autowired
constructor(
val paymentRepository: PaymentRepository
) {
#Transactional
open fun parseRegistryFileStream(input: InputStream): LinkedList<Pair<Long, String>> {
...
val registry = paymentRepository.findUnconfirmedTransactions(start, PERIOD)
...
}
}
While running test instead of calling my method real paymentRepository method is being called. I don't understand what could be the reason. LocalDate and Days are from Joda time and finally this is paymentRepository class:
#Repository
#Transactional
open class PaymentRepository : AbstractRepository<Payment, Long>(Payment::class.java) {
fun findUnconfirmedTransactions(start: LocalDate, days: Days): ScrollableResults = criteria().add(
and(
eq("isConfirmed", false),
ge("transactionDateTime", start),
lt("transactionDateTime", start.plus(days))
)).setCacheMode(CacheMode.IGNORE).scroll(ScrollMode.FORWARD_ONLY)
}
Please try this:
open fun findUnconfirmedTransactions(start: LocalDate, days: Days): ScrollableResults
Mocking needs to extend function and Spock won't be able to do so unless function is open in Kotlin.