Unit test filter with a mocked service - unit-testing

I'm using Grails 2.3.8 and trying to create a unit test for a filter that uses a service.
The filter:
class LicenseFilters {
def licenseService
def filters = {
all(controller:'*', action:'*') {
before = {
if(!licenseService.checkLicense()){
redirect(controller:"licenseExpired")
return false
}
}
}
}
}
The spec, first attempt:
#TestFor(ExecutionTraceController)
#Mock(LicenseFilters)
class LicenseFiltersSpec extends Specification{
void "Test filter redirects when license is wrong"() {
given:
LicenseFilters bean=applicationContext.getBean("com.nortia.sgmentia.license.LicenseFilters")
bean.licenseService=this.buildLicenseServiceStub(false)
when:
withFilters(action:"list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
private LicenseService buildLicenseServiceStub(boolean ok){
LicenseService result=Stub(LicenseService)
result.checkLicense() >> ok
return result
}
}
But it turns out (by debugging) that the bean that I grab from the context it is NOT the same one that receives the request thus I still get a NPE.
In a second attempt I try using defineBeans:
void "Test filter redirects when license is wrong"() {
given:
defineBeans {
licenseService(MethodInvokingFactoryBean){
targetObject = this
targetMethod= "buildLicenseServiceStub"
arguments=[false]
}
}
when:
withFilters(action:"list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
But the mocked bean is neither bean instanciated nor inyected.
Should I try to inyect the service manually into the filter??
There was this issue https://jira.grails.org/browse/GRAILS-8976 but it is closed.

I came across a similar situation and was able to fix it by adding the service to the #Mock annotation, i.e. #Mock([LicenseFilters, LicenseService]).
In your case the spec would look something like the following:
#TestFor(ExecutionTraceController)
#Mock([LicenseFilters, LicenseService])
class LicenseFiltersSpec extends Specification {
void "Test filter redirects when license is wrong"() {
given:
defineBeans {
licenseService(MethodInvokingFactoryBean) {
targetObject = this
targetMethod = "buildLicenseServiceStub"
arguments = [false]
}
}
when:
withFilters(action: "list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
private LicenseService buildLicenseServiceStub(boolean ok) {
LicenseService result = Stub(LicenseService)
result.checkLicense() >> ok
return result
}
}
Note: that mocking the service in this manner will, by default, inject an instance of the actual LicenseService into your filter. So, if the above defineBeans block is removed the actual implementation of LicenseService.checkLicense() will be called.

I finally found a workaround to make it work going with the second approach (using defineBeans).
The service is not being autowired into the filter so I finally did it manually with a pseudo-singleton:
class LicenseFilters {
def licenseService
def filters = {
all(controller:'*', action:'*') {
before = {
if(!this.licenseService){
this.licenseService=applicationContext.getBean("licenseService")
}
if(!this.licenseService.checkLicense()){
redirect(controller:"licenseExpired")
return false
}
}
}
}
}
Quite ugly but a solution at least.
Hope it helps someone out there.

Related

Ktor+Koin: Why unit test still keep connect to mongodb even I mocked the service that injected with `CoroutineDatabase` instance

Note: I'm a Kotlin beginner
Developing stack
Ktor
Koin
KMongo: MongoDB
I defined my application to be like this
Route -> Service -> Repository
Route is define all HTTP request endpoints.
Service is business logic of application.
Repository is data access layer for query/persist data to MongoDB.
What I did to test a route, if the simple route like.
fun Route.healthCheck() {
get("/health") {
call.respond(HealthCheckResponse(message = "OK"))
}
}
#Test
fun testHealth() = testApplication {
application {
configureRouting()
}
client.get("/health").apply {
expect {
that(status).isEqualTo(HttpStatusCode.OK)
that(contentType()?.contentType).isNotNull().and {
contains(ContentType.Application.Json.contentType)
}
that(Json.decodeFromString(HealthCheckResponse.serializer(), bodyAsText()).message)
.isEqualTo("OK")
}
}
}
Test above will run good.
But when the case of the route that has DI, injecting CoroutineDatabase object into Repository then that repository inject it to Service and the service inject into Route.
In unit test code, I defined like below.
// Route define
fun Application.configureRouting() {
routing {
user()
}
}
fun Route.user() {
val userService: UserService by inject()
...
}
class UserServiceImpl(
private val userRepository: UserRepository // <- Repository is injected with MongoDB `CoroutineDatabase` object.
) : AccountService {
...
}
===============
// Unit Test
class UserEndpointTest: KoinTest {
#get:Rule
val koinTestRule = KoinTestRule.create {
modules(module{ single { accountService } })
}
#Test
fun testUserEndpoint() = testApplication {
application {
configureRouting() // -> collecting all extended function of `Route`
}
val client = createClient {
install(ContentNegotiation) {
json()
}
}
val testerEmail = "i_am_testing#the-email.dev"
val requestJson = """
{
"email": "$testerEmail",
"password": "aBCdEf9"
}
""".trimIndent()
val testBody = jsonMapper.decodeFromString(CreateAccountRequest.serializer(), requestJson)
coEvery { mockAccountService.submitUser(any()) } returns User(id = newId(), email = testerEmail, password = "")
client.post("$accountEndpoint/user") {
contentType(ContentType.Application.Json)
setBody(testBody)
}.apply {
expect {
that(status).isEqualTo(HttpStatusCode.Created)
that(contentType()?.contentType).isNotNull().and {
contains(ContentType.Application.Json.contentType)
}
that((body() as AccountResponse).id).isNotBlank()
that((body() as AccountResponse).email).isNotBlank().and {
isEqualTo(testerEmail)
}
}
}
}}
I expected I did mocking service and it should be inject into Route then it won't chaining call to repository and CoroutineDatabase object.
But when I run it keep connecting to database, I noticed with log below.
2022-09-07 02:51:30.093 [cluster-ClusterId{value='631788a0b273ac122eaa8350', description='null'}-localhost:27017] DEBUG org.mongodb.driver.cluster - Updating cluster description to {type=UNKNOWN, servers=[{address=localhost:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {java.net.ConnectException: Connection refused}}]
And I tried to find out a tutorial to find how to write Unit Test with scenario like these, but I found most of answers on searched results are before Ktor version up. (Noticed from their codes still using withTestApplication {} which is deprecated already in version 2.1.0
Who can explain the step of Koin DI work and how to interrupt it with mock.
Thanks.

Grails 2.4.5 controller unit test verify transaction has rolled back

Here is a simple controller
class UserController {
#Transactional
def update() {
try {
....
throw new Exception("test transaction rollback")
...
} catch( e ) {
transactionStatus.setRollbackOnly()
respond "{ "status":"error" }"
}
}
}
Here is a unit test spec
#TestFor(UserController)
class UserControllerSpec extends Specification {
when:''
controller.update()
then:'verify that transaction has been rolled back'
// it fails with missing property, whats the best way to check that it was rolled back ?
controller.transactionManager.transactionRolledBack == true
}
whats the best way to test that the transaction has been rolled back ?
After #BurtBeckwith suggestion tried an integration spec.
// Domain User.groovy
class User { String firstName; String lastName }
// UserController.groovy
class UserController {
static responseFormats = ['json', 'xml']
static allowedMethods = [update: 'PUT']
#Transactional
def update( UserCommand cmd ) {
User m = User.get(id)
m.firstName=cmd.firstName
m.lastName=cmd.lastName
m.save(failOnError:true,flush:true)
transactionStatus.setRollbackOnly()
}
}
class UserCommand { Long id; String firstName; String lastName }
// IntegrationSpec UserControllerIntegrationSpec.groovy
import grails.test.spock.IntegrationSpec
class UserControllerIntegrationSpec extends IntegrationSpec {
def controller
def sessionFactory
def setup() {
controller = new UserController()
}
void "test transaction rollback"() {
given:'a user'
User u = new User(firstName:'oldFirstName',lastName:'oldLastName')
u.save(flush:true,failOnError:true)
controller.request.method='PUT'
controller.request.contentType = "application/json"
controller.request.content = '{"id":u.id,"firstName":"newFirstName","lastName":"newLastName"}'.getBytes()
when:'an update that rollsback is called'
controller.update()
// clear so that we do not get data from cache.
sessionFactory.currentSession.flush()
sessionFactory.currentSession.clear()
then:'we should still see old data'
User u2 = User.get(u.id)
// The following fails, u.firstName has 'newFirstName' which is wrong on rollback
u2.firstName == 'oldFirstName'
u2.lastName == 'oldLastName'
}
}
// DataSource.groovy
test {
dataSource {
url = "jdbc:mysql://localhost/starter_app_test"
driverClassName = "com.mysql.jdbc.Driver"
pooled = true
properties {
...
defaultAutoCommit=false
}
}
}
Any ideas why in integration test data seems to get persisted even when we rollback ?

Grails / Spock: How to mock single method within class where method is called from within the class itself?

Given the following, how do I mock processMessage() using Spock, so that I can check that processBulkMessage() calls processMessage() n times, where n is the number of messages within a BulkMessage?
class BulkMessage {
List messages
}
class MyService {
def processBulkMessage(BulkMessage msg) {
msg.messages.each {subMsg->
processMessage(subMsg)
}
}
def processMessage(Message message) {
}
}
You can use spies and partial mocks (requires Spock 0.7 or newer).
After creating a spy, you can listen in on the conversation between the caller and the real object underlying the spy:
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
subscriber.receive(_) >> "ok"
Sometimes, it is desirable to both execute some code and delegate to the real method:
subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" }
In my opinion this is not a well designed solution. Tests and design walk hand in hand - I recommend this talk to investigate it better. If there's a need to check if other method was invoked on an object being under test it seems it should be moved to other object with different responsibility.
Here's how I would do it. I know how visibility works in groovy so mind the comments.
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class MessageServiceSpec extends Specification {
def 'test'() {
given:
def service = new MessageService()
def sender = GroovyMock(MessageSender)
and:
service.sender = sender
when:
service.sendMessages(['1','2','3'])
then:
3 * sender.sendMessage(_)
}
}
class MessageSender { //package access - low level
def sendMessage(String message) {
//whatever
}
}
class MessageService {
MessageSender sender //package access - low level
def sendMessages(Iterable<String> messages) {
messages.each { m -> sender.sendMessage(m) }
}
}
It does not use Spock built-in Mocking API (not sure how to partially mock an object), but this should do the trick:
class FooSpec extends Specification {
void "Test message processing"() {
given: "A Bulk Message"
BulkMessage bulk = new BulkMessage(messages: ['a', 'b', 'c'])
when: "Service is called"
def processMessageCount = 0
MyService.metaClass.processMessage { message -> processMessageCount++ }
def service = new MyService()
service.processBulkMessage(bulk)
then: "Each message is processed separately"
processMessageCount == bulk.messages.size()
}
}
For Java Spring folks testing in Spock:
constructorArgs is the way to go, but use constructor injection. Spy() will not let you set autowired fields directly.
// **Java Spring**
class A {
private ARepository aRepository;
#Autowire
public A(aRepository aRepository){
this.aRepository = aRepository;
}
public String getOne(String id) {
tryStubMe(id) // STUBBED. WILL RETURN "XXX"
...
}
public String tryStubMe(String id) {
return aRepository.findOne(id)
}
public void tryStubVoid(String id) {
aRepository.findOne(id)
}
}
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository])
when:
a.getOne()
then:
// Stub tryStubMe() on a spy
// Make it return "XXX"
// Verify it was called once
1 * a.tryStubMe("1") >> "XXX"
}
}
Spock - stubbing void method on Spy object
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository]) {
1 * tryStubVoid(_) >> {}
}
when:
...
then:
...
}
}

Inject Stub Collaborators In Globally Stubbed Class Using Spock and Grails

I'm rendering a GSP in a Spock unit test with Grails 2.4.4. The GSP has a number of custom taglibs, and many of them call external services. The problem is that I can't inject a stubbed service into those taglibs, and when I call the service, it's always null. Here's what I'm trying to get simplest case working:
#TestMixin(GroovyPageUnitTestMixin)
#Mock(FooTagLib)
class MyGspSpec extends Specification {
def setup() {
def barStub = Stub(BarService)
def tagLibStub = GroovyStub(FooTagLib, global:true) {
}
tagLibStub.barService = barStub
}
def 'test method'() {
when: String result = render('myView', model:[:])
then: assert result != null
}
}
Taglib:
class FooTagLib {
static String namespace = "baz"
BarService barService
Closure<StreamCharBuffer> foo = { GroovyPageAttibutess attrs ->
out << barService.something()
}
}
_foo.gsp contents:
<baz:foo/>
I've also tried this:
FooTagLib.metaClass.barService = Stub(BarService) //also tried GroovyStub
I even tried putting a getter on the taglib and stubbing that, but it didn't work either:
In setup:
def barService = Stub(BarService)
GroovyStub(FooTagLib, global:true) {
getBarService() >> barService
}
In taglib:
BarService barService
BarService getBarService() { return barService }
//....
out << getBarService().something()
Finally, the only thing that worked (with the getter) is this:
FooTagLib.metaClass.getBarService = { -> return Stub(BarService) }
But this seems like a really bad hack.
I'm not sure how I can inject a stubbed version of it into the taglib. In my mind at least one of these should work, but obviously I'm doing something wrong.
This is how I would pass unit specs for the tagLib:
#TestFor(FooTagLib)
class FooTagLibSpec extends Specification {
def setup() {
// tagLib is available by default of above annotation is used
tagLib.barService = Mock(BarService) {
// Mock the service and stub the response
1 * serviceMethod() >> { "Hello World Returns" }
}
}
void "test something"() {
expect: 'applying template would result in mocked response'
applyTemplate('<baz:foo/>') == "Hello World Returns"
}
void "test taglib method call"() {
expect:
tagLib.foo() == "Hello World Returns"
}
}
class FooTagLib {
static defaultEncodeAs = [taglib:'html']
static String namespace = "baz"
BarService barService
Closure<StreamCharBuffer> foo = { attrs ->
out << barService.serviceMethod()
}
}
class BarService {
def serviceMethod() { "Hello World" }
}
I hope I was able to meet the checkpoints you were looking for in your tests. You can also see spec is able to mock BarService without issues. Give a shout if you need for info.

How do you test default sort in a grails unit test with mocked domains

Is it possible to test the sort propertyName which is defined in the staticMappingBlock?
This works during the integration phase but not during the unit phase where my domain has:
static mapping = {
sort 'lastName'
}
void testDefaultSortOrder(){
def agent1 = new CommissionAgent(firstName: 'fred', lastName: 'b', active:true).save()
def agent2 = new CommissionAgent(firstName: 'fred2', lastName:'a', active:false).save()
def agents = CommissionAgent.list()
assertEquals 'sort order is wrong', agents[0].lastName, agent2.lastName
assertEquals 'sort order is wrong', agents[1].lastName, agent1.lastName
}
Grails version is 1.3.1
There isn't any good way to test the actual sorting in unit tests that I'm aware of. What you're trying to test is really such an integral part of the GORM integration that testing it on mocked domain objects, even if they support the sort mapping, doesn't test the actual code that will be run.
The closest thing that you could do in a unit test would be to take a look at the static mapping object to assert that the value of "sort" is set to what you expect it to be.
I put together a blog post a while ago on how to interrogate groovy closures for values. This technique could be used to assert the sort order is set to what you expect like this:
Foo domain object:
package com.example
class Foo {
String name
static mapping = {
sort "name"
}
}
FooTests unit test:
package com.example
import grails.test.*
class FooTests extends GrailsUnitTestCase {
void testFooSort() {
def mappingValues = ClosureInterrogator.extractValuesFromClosure(Foo.mapping)
assertEquals "name", mappingValues.sort
}
}
ClosureInterrogator class that allows you to see what a closure does:
package com.example
class ClosureInterrogator {
private Map closureValueMap = [:]
static Map extractValuesFromClosure(Closure closure) {
def interrogator = new ClosureInterrogator(closure)
return interrogator.closureValueMap
}
private ClosureInterrogator(Closure closure) {
def oldResolveStrategy = closure.getResolveStrategy()
def oldDelegate = closure.getDelegate()
closure.delegate = this
closure.resolveStrategy = Closure.DELEGATE_FIRST
try {
closure()
} finally {
closure.setDelegate(oldDelegate)
closure.setResolveStrategy(oldResolveStrategy)
}
}
// property getter
def propertyMissing(String name) {
return closureValueMap[name]
}
// property setter
def propertyMissing(String name, value) {
closureValueMap[name] = value
}
def methodMissing(String name, args) {
if (args.size() == 1) {
closureValueMap[name] = args[0]
} else {
closureValueMap[name] = args
}
}
}