MediatorLiveData doesn't work in JUnit tests? - unit-testing

So I've tried using MediatorLiveData for the rather simple use-case of converting an ISO country code (e.g. "US") to a country calling code (e.g. "+1") through the use of libphonenumber. The resulting screen works fine, but seems to fail JUnit tests, even when InstantTaskExecutorRule is used.
Example minimal unit test (in Kotlin) that I believe should pass, but fails instead:
import android.arch.core.executor.testing.InstantTaskExecutorRule
import android.arch.lifecycle.MediatorLiveData
import android.arch.lifecycle.MutableLiveData
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
class MediatorLiveData_metaTest {
#get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
#Test
fun mediatorLiveData_metaTest() {
val sourceInt = MutableLiveData<Int>()
val mediatedStr = MediatorLiveData<String>()
mediatedStr.addSource(sourceInt) {
mediatedStr.value = it.toString()
}
sourceInt.value = 123
assertEquals("123", mediatedStr.value) // says mediatedStr.value is null
}
}

Thanks to Reddit user matejdro; the answer was that like Schrödinger's proverbial cat, MediatorLiveData won't update itself unless observed, so I'd need a mediatedStr.observeForever{} to force it to update itself.

Related

Is it possible to mock a controller and have access to the FormTagLib in the same unit test?

As the title states, is it possible to have a unit test for a controller, and mock a tag lib?
As it stands, I have a User controller. Many of the actions use the
g.message(code: 'something.something')
call to set a message on the page.
#TestFor(UserController)
#Mock(User)
#TestMixin(GroovyPageUnitTestMixin)
class UserControllerSpec extends Specification
{
UserService userServiceMock = Mock(UserService)
def setup()
{
controller.userService = userServiceMock
}
def cleanup()
{
}
void "test manageDevice"()
{
given:
def g = mockTagLib(FormTagLib)
when:
controller.manageDevice()
then:
model.pageTitle == 'message.device'
}
With that code I'm trying to hit the controller action but because of the g.message, it's failing with an error saying that it can't set a value to null. Pretty much because it doesn't see the "g.message"
I'm a little unsure if my unit test needs written differently, or if I'm just missing something.
Any help would be great!
EDIT:
Some updates using messageSource:
void "test manageDevice"()
{
given:
messageSource.addMessage 'user.devices', request.locale, 'Manage Devices'
when:
controller.manageDevices()
then:
assertEquals model.pageTitle == 'user.devices', controller.flash.message
}
It seems to still be complaining because it doesn't have context of the "g" namespace on the controller. I'll note as well, I don't have context of 'addMessage' from messageSource. Not sure why, it should be there.
In the same controller, and many others, the only taglib we use in the controller scope is 'g' for the 'g.message' set in each action. The only other call that's being done, is one using the 'g' for a call like 'g.fixRedisIssue'
You can make a unit test for a controller with a mocked TagLib by including the necessary TagLib classes in the value of the grails.test.mixin.Mock annotation on the Spec class. For example, if you have the following TagLib:
(based on code in Grails documentation)
package org.grails.samples
class SimpleTagLib {
static namespace = 'g'
def hello = { attrs, body ->
out << "Hello ${attrs.name ?: 'World'}"
}
}
And you have the following controller:
package org.grails.samples
class SimpleController {
def flashHello() {
flash.message = g.hello()
}
}
You could test it with the following specification:
package org.grails.samples
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(SimpleController)
#Mock(SimpleTagLib)
class SimpleControllerSpec extends Specification {
void 'test flashHello'() {
when:
controller.flashHello()
then:
flash.message == 'Hello World'
}
}
If you have multiple classes to mock, you can specify them in an array argument to #Mock. So you can add any necessary tag libraries to UserControllerSpec by changing #Mock(User) to something like #Mock([User, FormTagLib, YourFixRedisIssueTagLib]). However, I'd be surprised if you actually did need to add FormTagLib because in my Grails testing it seems to be included by default.
As some further advice, some of the code you posted doesn't make any sense. Specifically:
assertEquals model.pageTitle == 'user.devices', controller.flash.message
Decide specifically what you want to test for and focus on writing the simplest code possible for that.
Also, based on your description of what's happening, I think that you're getting thrown off because your IDE is not properly integrated with Grails to see the g context.
Instead of returning the message text to the view from the controller, why not return the message code instead? For example, if you have something like this in your view:
<p>${flash.message}</p>
You can replace it with this:
<p><g:message code="${flash.code}" /></p>
And then set the code in the controller:
flash.code = "user.devices"
Then you'll be able to test the controller methods painlessly :)

how to assert values inside "render" in unit test case (grails ,junit)

I was trying to assert the values inside "render" in my unit test cases written in Grails. But it does not seems to be proper
render(view:"create",model[:])
what i tried was
assertEquals("create",renderArgs("view"))
i also tried some alternatives like controller.response.renderedUrl etc. But none of those are working.
Could someone give an idea?
Thanks in advance,
BK
To test the view you can simply use an implicit view variable, though it will point to the path of your view/template, e.g. /controller/create. So you could write assertEquals(view, '/controller/create'). There is also an implicit model variable for which you can proceed similarly.
See docs (Testing View Rendering section).
The following tests work:
grails-app/controllers/demo/DemoController.groovy:
package demo
class DemoController {
def index() {
render view: 'first'
}
}
test/unit/demo/DemoControllerSpec.groovy:
package demo
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(DemoController)
class DemoControllerSpec extends Specification {
void "test render view"() {
when:
controller.index()
then:
'/demo/first' == view
}
}
test/unit/demo/DemoControllerTests.groovy:
package demo
import grails.test.mixin.TestFor
#TestFor(DemoController)
class DemoControllerTests {
void testRenderView() {
controller.index()
assert '/demo/first' == view
}
}
I hope that helps.

Grails integration test - redirect action returns null

This is a small integration Junit that I'm having difficulty with. I've re-written this several different ways and the current way is straight out of the Grails manual - but it still returns null. I don't see the error; I thought it might be a spelling error but I've checked all those. I've tried redirectUrl and redirectedUrl - still returns null.
Controller snippet:
#Transactional(readOnly = true)
def saveReportError() {
redirect(action:'reportError')
}
Test:
#Test
void "test save error report"() {
controller.saveReportError()
assertEquals '/reportSiteErrors/reportError', controller.response.redirectUrl
}
I recommend to implement the test as a unit test like this.
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(SimpleController)
class SimpleControllerSpec extends Specification {
void 'test index'() {
when:
controller.index()
then:
response.redirectedUrl == '/simple/hello'
}
}
Using a unit test has the advantage of speed.

How to mock a request when unit testing a service in grails

I am trying to unit test a service that has a method requiring a request object.
import org.springframework.web.context.request.RequestContextHolder as RCH
class AddressService {
def update (account, params) {
try {
def request = RCH.requestAttributes.request
// retrieve some info from the request object such as the IP ...
// Implement update logic
} catch (all) {
/* do something with the exception */
}
}
}
How do you mock the request object ?
And by the way, I am using Spock to unit test my classes.
Thank you
This code seems to work for a basic unit test (modified from Robert Fletcher's post here):
void createRequestContextHolder() {
MockHttpServletRequest request = new MockHttpServletRequest()
request.characterEncoding = 'UTF-8'
GrailsWebRequest webRequest = new GrailsWebRequest(request, new MockHttpServletResponse(), ServletContextHolder.servletContext)
request.setAttribute(GrailsApplicationAttributes.WEB_REQUEST, webRequest)
RequestContextHolder.setRequestAttributes(webRequest)
}
It can be added as a function to your standard Grails unit test since the function name does not start with "test"... or you can work the code in some other way.
One simple way to mock these, is to modify the meta class for RequestContextHolder to return a mock when getRequestAttributes() is called.
I wrote up a simple spec for doing this, and was quite surprised when it didn't work! So this turned out to be a quite interesting problem. After some investigation, I found that in this particular case, there are a couple of pitfalls to be aware of.
When you retrieve the request object, RCH.requestAttributes.request, you are doing so via an interface RequestAttributes that does not implement the getRequest() method. This is perfectly fine in groovy if the returned object actually has this property, but won't work when mocking the RequestAttributes interface in spock. So you'll need to mock an interface or a class that actually has this method.
My first attempt at solving 1., was to change the mock type to ServletRequestAttributes, which does have a getRequest() method. However, this method is final. When stubbing a mock with values for a final method, the stubbed values are simply ignored. In this case, null was returned.
Both these problems was easily overcome by creating a custom interface for this test, called MockRequestAttributes, and use this interface for the Mock in the spec.
This resulted in the following code:
import org.springframework.web.context.request.RequestContextHolder
// modified for testing
class AddressService {
def localAddress
def contentType
def update() {
def request = RequestContextHolder.requestAttributes.request
localAddress = request.localAddr
contentType = request.contentType
}
}
import org.springframework.web.context.request.RequestAttributes
import javax.servlet.http.HttpServletRequest
interface MockRequestAttributes extends RequestAttributes {
HttpServletRequest getRequest()
}
import org.springframework.web.context.request.RequestContextHolder
import spock.lang.Specification
import javax.servlet.http.HttpServletRequest
class MockRequestSpec extends Specification {
def "let's mock a request"() {
setup:
def requestAttributesMock = Mock(MockRequestAttributes)
def requestMock = Mock(HttpServletRequest)
RequestContextHolder.metaClass.'static'.getRequestAttributes = {->
requestAttributesMock
}
when:
def service = new AddressService()
def result = service.update()
then:
1 * requestAttributesMock.getRequest() >> requestMock
1 * requestMock.localAddr >> '127.0.0.1'
1 * requestMock.contentType >> 'text/plain'
service.localAddress == '127.0.0.1'
service.contentType == 'text/plain'
cleanup:
RequestContextHolder.metaClass = null
}
}

How to run scala tests with junit 4?

I can't run following code with IDEA
#Test
class CompanyURLTest extends Assert {
#Test
def test = assert(false);
}
It runs, but J-Unit says that there are not test to run
I generally use ScalaTest in combination with the Junit4 runner so that Maven sees and executes my tests. I like the Spec/FlatSpec/WordSpec semantics for organizing tests. I'm experimenting with the ShouldMatchers but I have used JUnit for so long that asserts just seem a bit more natural to me.
Here's an example:
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
#RunWith(classOf[JUnitRunner])
class BlogFeedHandlerTest extends FlatSpec with ShouldMatchers with Logging {
"the thingy" should "do what I expect it to do" in {
val someValue = false;
assert(someValue === false)
}
}
The ScalaTest docs are at http://www.scalatest.org/
The following works for me
import org.junit._
import Assert._
class MyTest {
#Test
def test = assert(false)
}
The #org.junit.Test annotation is only applicable for methods: #Target({ElementType.METHOD}).
Also keep in mind that test methods must return Unit.
import org.junit.Assert._
import org.junit.Test
class CompanyURLTest {
#Test def test = assertFalse(false)
}
Even though it's late - Gradle site surprisingly has a lot of good tutorials showing how to support Scala tests.
https://guides.gradle.org/building-scala-libraries/#review_the_generated_project_files