Roboelectric with Room Database for android - android-room

How to do unit testing of Room Database with the help of Roboeletric?
I don't want to do instrumentation testing.

From what I can tell it can be done like this
//#RunWith(AndroidJUnit4::class)
#RunWith(RobolectricTestRunner::class)
class WordDaoTest {
private lateinit var wordRoomDatabase: WordRoomDatabase
private lateinit var wordDao: WordDao
#get:Rule
var instantTaskExecutor = InstantTaskExecutorRule()
#Before
fun createDb() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
wordRoomDatabase = Room.inMemoryDatabaseBuilder(context, WordRoomDatabase::class.java).allowMainThreadQueries().build()
wordDao = wordRoomDatabase.wordDao()
wordRoomDatabase.wordDao().insertAll(listOf<Word(Word("one"),Word("two"),Word("three"))
}
#After
fun closeDb() {
wordRoomDatabase.close()
}
#Test
fun testGetName() {
Assert.assertThat(getValue(wordDao.getAllLiveWords()).size, equalTo(3))
}
}
It seems though you need allowMainThreadQueries() in the build of the DB.
I am not sure as to why everyone tests the Dao in the instrumentation test when it can be done in the Unit Test and then be added to code coverage (maybe someone else has some insight)
This code is in Kotlin but I am sure it would translate to java just the same.
This was provided to me however as to why it's not considered to be best practice
https://developer.android.com/training/data-storage/room/testing-db
Note: Even though this setup allows your tests to run very quickly, it isn't recommended because the version of SQLite running on your device—and your users' devices—might not match the version on your host machine.

Robolectric can support such JVM unit tests with Room.
To get the required context, please add below dependency in your build.gradle:
testImplementation 'androidx.test:core:1.2.0'
Let's assume that we have a repository class which wraps the Room Dao. And here's a quick example:
#RunWith(RobolectricTestRunner::class)
#Config(sdk = [28]) // This config setting is the key to make things work
class FooRepositoryTest {
#get:Rule
var instantTask = InstantTaskExecutorRule()
private lateinit var database: MyDatabase
private lateinit var sut: FooRepository
#Before
fun setUp() {
val context = ApplicationProvider.getApplicationContext<Context>()
database = Room.inMemoryDatabaseBuilder(context, MyDatabase::class.java)
.allowMainThreadQueries()
.build()
sut = FooRepository(database.fooDao())
}
#After
fun tearDown() {
database.close()
}
}

Related

How to stop #CucumberContextConfiguration with #SpringBootTest from reloading application context between every test?

I've got this problem where my application context is reloaded between every test. I'm wiring in my actual application with functional test properties, wiremock etc. to create a functional test environment. Tests have always run fine but now we've added several it's become painfully slow due to the spring application being re-run everytime. The io.cucumber versions I'm using in my pom for cucumber-spring, cucumber-java, cucumber-junit is 7.11.1.
My Functional Test runner is annotated like this:
#RunWith(Cucumber.class)
#CucumberOptions(
features = "classpath:functional/features",
glue = {"com.iggroup.ds.functional.stepdefinitions"},
monochrome = true,
tags = "#FunctionalTest",
plugin = {"pretty", "html:target/cucumber-html-report", "junit:target/cucumber-xml-report.xml"}
)
public class FunctionalTestRunner {
#BeforeClass
public static void beforeClass() {
prepareEnvironment();
}
private static void prepareEnvironment() {
int applicationPort = SocketUtils.findAvailableTcpPort();
System.setProperty("server.port", String.valueOf(applicationPort));
System.setProperty("spring.active.profiles", "FUNCTIONAL_TEST");
System.setProperty("spring.cloud.config.enabled", "false");
System.setProperty("spring.cloud.config.server.bootstrap", "false");
}
}
Inside my glue package the Cucumber Configuration looks like this:
#AutoConfigureWireMock(port = 8089)
#CucumberContextConfiguration
#SpringBootTest(
classes = {
ServiceApplication.class,
RestClients.class
},
webEnvironment = DEFINED_PORT,
properties = {
"spring.profiles.active=FUNCTIONAL_TEST",
"spring.cloud.config.enabled = false"
}
)
public class FunctionalTestSpringCucumberConfiguration {
}
And lastly the application itself looks like this:
#EnableAsync
#EnableCaching
#EnableConfigServer
#SpringBootApplication
#EnableConfigurationProperties
public class ServiceApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
I had read somewhere before that the presence of #MockBean was causing unexpected refreshes between context although I never found out as to why - but I have none defined. As far as I can tell across the articles I've been reading, this shouldn't refresh my context every time so wondering if there's any way I can force it not to rewire the ServiceApplication.class in between every scenario?
#AutoConfigureWireMock(port = 8089)
By using Wiremock on fixed port you are dirtying the application context. This means a new application context will be created for each test. The code responsible for this prints a warning that you can see in your logs.
if (portIsFixed(testContext)) {
if (log.isWarnEnabled()) {
log.warn("You've used fixed ports for WireMock setup - "
+ "will mark context as dirty. Please use random ports, as much "
+ "as possible. Your tests will be faster and more reliable and this "
+ "warning will go away");
}
testContext.markApplicationContextDirty(DirtiesContext.HierarchyMode.EXHAUSTIVE);
}

Spring MockMvc second test fails when using StreamingResponseBody

I have a simple Spring Boot service that uses StreamingResponseBody to write the response.
When testing this with MockMvc, the first test gives me the correct response, but the second one has an empty response body.
However, when I add a little sleep time to the test, they both work.
Here is my controller (Kotlin)
#RestController
class DummyController() {
#GetMapping()
fun streamIndex() =
ResponseEntity.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(StreamingResponseBody { it.write("Hello world".toByteArray()) })
}
And my tests:
#AutoConfigureMockMvc(print = MockMvcPrint.NONE)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DummyControllerTest {
private val controller = DummyController()
private val mvc = MockMvcBuilders.standaloneSetup(controller).setControllerAdvice(GlobalExceptionHandler()).build()
#Test
fun test1() {
mvc.perform(get("/")).andExpect(status().isOk).andExpect(content().string("Hello world"))
}
#Test
fun test2() {
mvc.perform(get("/")).andExpect(status().isOk).andExpect(content().string("Hello world"))
}
}
Thee first test succeeds, the second one fails with:
java.lang.AssertionError: Response content expected:<Hello world> but was:<>
Then when I add a little delay in the test it works:
#Test
fun test2() {
mvc.perform(get("/")).andExpect(status().isOk)
.andDo { Thread.sleep(5) }
.andExpect(content().string("Hello world"))
}
Is this a bug in MockMvc or something missing in my test?
Thanks in advance,
Rob
Instead of adding a delay, you will need to fetch the async result first by doing the following:
#Test
fun test2() {
mvc.perform(get("/"))
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk)
.andExpect(content().string("Hello world"))
}
Make sure you add this to both tests.
Source: https://github.com/spring-projects/spring-framework/blob/main/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java #line 81
Your test is wrong/weird. You have #SpringBootTest which starts an app on the port, have configured #AutoConfigureMockMvc and totally ignore all of that.
The combination in your #SpringBootTest wouldn't even work as you cannot use RANDOM_PORT with MockMvc (that only works with a MOCK environment). You either use the MOCK environment or switch to TestRestTemplate/TestWebClient for testing.
You probably worked around the issues you had with manually constructing a controller and manually registering MockMvc with that controller.
Finally you are using an async request which you would need to consume properly as well. See async testing in the Spring Reference Guide. It also shows another way of testing using the WebTestClient which is preferred for streaming responses.
In short your test is a bit af a frankenstein test.
I would strongly suggest to use the technologies in a proper way and rewrite your test as follows.
#WebMvcTest(DummyController.class)
class DummyControllerTest {
#Autowired
private val mvc;
#Test
fun test1() {
var mvcResult = mvc.perform(get("/"))
.andExpect(request().asyncStarted())
.andExpect(status().isOk)
.andExpect(request().asyncResult("Hello world"))
}
#Test
fun test2() {
mvc.perform(get("/"))
.andExpect(request().asyncStarted())
.andExpect(status().isOk)
.andExpect(request().asyncResult("Hello world"))
}
}
This test won't start a full blown container but only the Web MVC stuff and the stuff needed for your controller. In the tests the async result is now also properly parsed and asserted. All in all it should now also run a lot faster due to the reduced things needed to start (unless there wasn't much to start with).

How can I test logs of Spring Boot application?

I have an application that is a mix of Spring Boot, Jersey, and Camel applications. It starts as a Spring Boot app. I am writing integration tests, and I need to make asserts on logs?
For instance, I need to assert that the Camel route read a message from source A. How can I make reliable asserts on logs? Is there any industry standard for this?
NOTE: I tried finding any solution, but at the moment, I neither understand how to solve it nor can find ready solutions.
UPDATE 1: The detail that I underestimated, but it seems important. I use Kotlin, NOT Java. I tried applying answer, but it isn't one to one transferable to Kotlin.
UPDATE 2:
This is a conversion from Java to Kotlin. ListAppender doesn't have enough information to resolve the type in Kotlin.
class LoggerExtension : BeforeEachCallback, AfterEachCallback {
private val listAppender: ListAppender<ILoggingEvent> = ListAppender<ILoggingEvent>()
private val logger: Logger = LoggerFactory.getLogger(ROOT_LOGGER_NAME) as Logger
override fun afterEach(extensionContext: ExtensionContext) {
listAppender.stop()
listAppender.list.clear()
logger.detachAppender(listAppender)
}
override fun beforeEach(extensionContext: ExtensionContext) {
logger.addAppender(listAppender)
listAppender.start()
}
val messages: List<String>
get() = listAppender.list.stream().map { e -> e.getMessage() }.collect(Collectors.toList())
val formattedMessages: List<String>
get() = listAppender.list.stream().map { e -> e.getFormattedMessage() }.collect(Collectors.toList())
}
Kotlin: Not enough information to infer type variable A
Not an error, but I have a feeling that it will fail in runtime:
private val logger: Logger = LoggerFactory.getLogger(ROOT_LOGGER_NAME) as Logger
Spring Boot comes with OutputCapture rule for JUnit 4 and OutputCaptureExtension for JUnit 5, that let you assert on text sent to standard output.
public class MyTest {
#Rule
public OutputCaptureRule output = new OutputCaptureRule();
#Test
public void test() {
// test code
assertThat(output).contains("ok");
}
}

#Transactional in Spring Boot - I believe prerequisites are covered (public, external invocation), but testing indicates no transaction

I'm trying to get a Kotlin function to operate transactionally in Spring Boot, and I've looked at several sources for information, such as https://codete.com/blog/5-common-spring-transactional-pitfalls/ and Spring #Transaction method call by the method within the same class, does not work?. I believe I have the prerequisites necessary for the #Transactional annotation to work - the function is public and being invoked externally, if my understanding is correct. My code currently looks like this:
interface CreateExerciseInstance {
operator fun invoke(input: CreateExerciseInstanceInput): OpOutcome<CreateExerciseInstanceOutput>
}
#Component
class CreateExerciseInstanceImpl constructor(
private val exerciseInstanceRepository: ExerciseInstanceRepository, // #Repository
private val activityInstanceRepository: ActivityInstanceRepository, // #Repository
private val exerciseInstanceStepRepository: ExerciseInstanceStepRepository // #Repository
) : CreateExerciseInstance {
#Suppress("TooGenericExceptionCaught")
#Transactional
override fun invoke(input: CreateExerciseInstanceInput): OpOutcome<CreateExerciseInstanceOutput> {
...
val exerciseInstanceRecord = ... // no in-place modification of repository data
val activityInstanceRecords = ...
val exerciseInstanceStepRecords = ...
return try {
exerciseInstanceRepository.save(exerciseInstanceRecord)
activityInstanceRepository.saveAll(activityInstanceRecords)
exerciseInstanceStepRepository.saveAll(exerciseInstanceStepRecords)
Outcome.Success(...)
} catch (e: Exception) {
Outcome.Failure(...)
}
}
}
My test currently looks like this:
#ExtendWith(SpringExtension::class)
#SpringBootTest
#Transactional
class CreateExerciseInstanceTest {
#Autowired
private lateinit var exerciseInstanceRepository: ExerciseInstanceRepository
#Autowired
private lateinit var exerciseInstanceStepRepository: ExerciseInstanceStepRepository
#Autowired
private lateinit var activityInstanceRepository: ActivityInstanceRepository
#Test
fun `does not commit to exercise instance or activity repositories when exercise instance step repository throws exception`() {
... // data setup
val exerciseInstanceStepRepository = mockk<ExerciseInstanceStepRepository>()
val exception = Exception("Something went wrong")
every { exerciseInstanceStepRepository.save(any<ExerciseInstanceStepRecord>()) } throws exception
val createExerciseInstance = CreateExerciseInstanceImpl(
exerciseInstanceRepository = exerciseInstanceRepository,
activityInstanceRepository = activityInstanceRepository,
exerciseInstanceStepRepository = exerciseInstanceStepRepository
)
val outcome = createExerciseInstance(...)
assert(outcome is Outcome.Failure)
val exerciseInstances = exerciseInstanceRepository.findAll()
val activityInstances = activityInstanceRepository.findAll()
assertThat(exerciseInstances.count()).isEqualTo(0)
assertThat(activityInstances.count()).isEqualTo(0)
}
}
The test fails with:
org.opentest4j.AssertionFailedError:
Expecting:
<1>
to be equal to:
<0>
but was not.
at assertThat(exerciseInstances.count()).isEqualTo(0). Is the function actually non-public or being invoked internally? Have I missed some other prerequisite?
This test doesn't say anything about your component not being transactional.
First, you create an instance yourself rather than using the one created by Spring. So Spring knows nothing about this instance, and can't possibly warp it into a transactional proxy.
Second, the component doesn't throw any runtime exception, So Spring doesn't rollback the transaction.

Testing Custom Gradle Plugins: Providing Configuration Data

I am trying to test a custom Gradle plugin that is configured via an Extension object that would normally be present in the build.gradle file.
For example, my build.gradle would normally look something like this:
{
apply plugin: 'foobarConfigurator'
[... stuff ...]
foobarConfig {
bar = 'boop'
baz = 'baap'
bot = 'faab'
}
[... stuff ...]
}
In my custom plugin class I have code that does this in the apply method:
def config = project.extensions.create('foobarConfig', FooBarConfig)
It's unclear to me how in a JUnit test I should write my test methods so that I can provide and test for different configuration values in a foobarConfiguration in a Project instance created by the ProjectBuilder class.
Any help appreciated, thanks!
If you intend to write a JUnit test for your extension, you can simply create an instance and configure it programmatically:
class FooBarConfigTest {
private FooBarConfig fooBarConfig
#Before
public void before() {
fooBarConfig = new FooBarConfig()
}
#Test
public void example() {
fooBarConfig.bar = 'boop'
assertEquals("expectedIfBarIsBoop", fooBarConfig.someMethod())
}
}
On the other hand, to test the plugin itself, you can use a ProjectBuilder and find the extension by type:
class MyPluginTest {
private MyPlugin plugin
private Project project
#Before
public void before() {
project = ProjectBuilder.builder().build();
plugin = new MyPlugin()
plugin.apply(project)
}
#Test
public void example() {
FooBarConfig foobarConfig = project.extensions.findByType(FooBarConfig)
assertNotNull(foobarConfig)
foobarConfig.bar = 'boop'
}
}

Resources