how to avoid a domain call in unit testing with grails / spock - unit-testing

I have the following lines of code
username = username.stripIndent()
user = User."${databaseInstance}".findByUsername(username)
if (user == null){
return "User does not exist"
}
I'm trying to unit test this functionality with:
def setup() {
def mockUser = Mock(User)
myClass.user = mockUser
}
void "userNotFoundGetUserInfo"(){
given:
myClass.databaseInstance = 'X'
_ * mockUser._ >> null
when:
def result = myClass.getUserInfo(username)
then:
result == "User does not exist"
}
but I keep getting an error of "No such property: X for class mypackage.User"
I realize this is because i'm mocking the "user" object and not the "User" class, so how do I get around the fact that my code is making a direct call to a domain class?

Use Grails built-in #Mock annotation instead of Spock's Mock() method. Go like:
#Mock(User)
class YourTestSpecification extends Specification {
def setup() {
myClass.user = new User()
}
}
#Mock is meant to mock Grails domain classes.

Related

How to mock passwordEncoder using Spock in a Grails unit test

I could use some advice in how to mock an auto wired dependency used in a Grails unit test. I've omitted most of the unnecessary code and just given the test class and the relevant methods in the file class under test
class UserService {
def springSecurityService // spring bean
def passwordEncoder // auto wired as per
//https://stackoverflow.com/questions/33303585/spring-//security-encode-password-with- bcrypt-algorithm
.....
def passwordPreviouslyUsed(String newPassword, def userId){
def passwordExists = false
def usersPasswords = findPasswordsForUser(userId)
usersPasswords.each{ password ->
if (passwordEncoder.isPasswordValid(oldPassword, newPassword, null)) {
passwordExists = true
}
}
return passwordExists
}
.....
def findPasswordsForUser(def userId){
User foundUser = User.findById(userId)
def passwordsForUser = UserPasswords.createCriteria().list {
eq('user', foundUser)
projections{
property('password')
}
}
passwordsForUser
}
My test
class UserServiceSpec extends Specification implements DataTest, ServiceUnitTest<UserService> {
def passwordEncoder
def setupSpec() {
mockDomains User, UserPasswords
}
def setup() {
def stubPasswordEncoder = Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
}
void "test for user passwordPreviouslyUsed"() {
given: "a user already exists"
setup()
service.createNewUser("testName", "testy#test.com", "Secret1234" )
//^(does some validation, then User.save())
User foundUser = User.findByEmail("testy#test.com")
foundUser.fullName == "testName"
long testUserId = foundUser.id
and: "we update the password for that user, and it to the userPasswords"
UserPasswords newUserPassword = new UserPasswords(
user: foundUser,
password: "newPassword1"
)
newUserPassword.save()
//use passwordPreviouslyUsed method to check a string with the same value as the
//previously
//updated password to check if it has already been used
when: "we check if the password has been used before"
def response = service.passwordPreviouslyUsed("newPassword1", fundsyId)
then:
response == true
}
Without stubbing or mocking this dependency, I get the error
Cannot invoke method isPasswordValid() on null object
I tried to stub password encoder and have it return true
def stubPasswordEncoder = Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
But this gives an error message:
Stub in 'spock.mock.MockingApi' cannot be applied to '(java.lang.Object, groovy.lang.Closure)'
Is there any way to mock this dependency with Spock?
Stub and Mock take a class - you're giving it an instance that is null - hence the exception.
You should be able to mock it as so:
def mockPasswordEncoder = Mock(PasswordEncoder)
// note this is the class
// org.springframework.security.crypto.password.PasswordEncoder
I tried enrichelgeson's approach, and it worked!
I first imported PasswordEncoder to the test class
import org.springframework.security.crypto.password.PasswordEncoder
then implemented the normal mocking procedure. I was initially confused because they class under test just implicitly created an instance of the class by defining it.
def stubPasswordEncoder = Stub(PasswordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
I also found another solution that didn't require mocking
service.passwordEncoder = [ isPasswordValid: { String rawPass, String salt, Null -> true } ]
Both approaches work fine. Thanks for the help!

How to mock springSecurityService in an unit test

I am unit testing a Grails controller method that internally creates an user instance. User domain class uses the springSecurityService of the Spring Security plugin to encode the password before inserting it into the database.
Is there a way to mock that springSecurityService from my unit test in order to get rid of that error?
Failure: Create new individual member(MemberControllerSpec)
| java.lang.NullPointerException: Cannot invoke method encodePassword() on null object
Please find my unit test below.
#TestMixin(HibernateTestMixin)
#TestFor(MemberController)
#Domain([User, IndividualPerson])
class MemberControllerSpec extends Specification {
void "Create new individual member"() {
given:
UserDetailsService userDetailsService = Mock(UserDetailsService)
controller.userDetailsService = userDetailsService
def command = new IndividualPersonCommand()
command.username = 'scott#tiger.org'
command.password = 'What ever'
command.firstname = 'Scott'
command.lastname = 'Tiger'
command.dob = new Date()
command.email = command.username
command.phone = '89348'
command.street = 'A Street'
command.housenumber = '2'
command.postcode = '8888'
command.city = 'A City'
when:
request.method = 'POST'
controller.updateIndividualInstance(command)
then:
view == 'createInstance'
and:
1 * userDetailsService.loadUserByUsername(command.username) >> null
and:
IndividualPerson.count() == 1
and:
User.count() == 1
cleanup:
IndividualPerson.findAll()*.delete()
User.findAll()*.delete()
}
}
One way to mock a service is to use Groovy's MetaClass
import grails.test.mixin.Mock
import grails.plugin.springsecurity.SpringSecurityService
...
#Mock(SpringSecurityService)
class MemberControllerSpec extends Specification {
def setupSpec() {
SpringSecurityService.metaClass.encodePassword = { password -> password }
}
def cleanupSpec() {
SpringSecurityService.metaClass = null
}
....
In this example, the call to SpringSecurityService.encodePassword() will simply return the password in plain text.
An approach using Mocks is discussed here.
You can to use this code to encode password in User:
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
When springSecurityService is null, encodePassword is not called and NPE is not raised
When you use controller unit test with spring security rest plugin in Grails v4/v3, if your controller method reference springSecurityService methods like 'athenticatedUser', there will be NullPointException, because springSecurityService is not autowired into the spring application context.
Add code like below, you can inject springSecurityService and mock it's methods.
class GuessControllerSpec extends Specification implements ControllerUnitTest<GuessController> {
#Override
Closure doWithSpring() {
return {
// mock method
SpringSecurityService.metaClass.getCurrentUser = {return new User()}
// inject into spring context
springSecurityService(SpringSecurityService)
}
}
...
}

Unit testing controllers in Play framework with SecureSocial

I am trying to invent some kind of mocking SecureSocial action generators, or SecureSocial itself to be able to unit-test controller methods.
I've found some approaches, like Unit-testing methods secured with Securesocial annotation and Testing a Play2 application with SecureSocial using dependency injection but the thing is, that in that questions authors, in fact, don't do unit testing, but integration testing.
My unit tests look like this:
trait MockDaoProvider extends IDaoProvider {
def entityDao = entityDaoMock
}
val controller = new MyController with MockDaoProvider
"MyController.list" should {
"return an OK" in {
entityDaoMock.list().returns(List())
val result = controller.list()(FakeRequest())
status(result) must equalTo(OK)
}
}
As one can see, I mocked dependencies to isolate and test the behavior that controller method actually does.
Everything was OK until I used SecuredAction from securesocial for MyController.list method. Now I get an exception, and the test fails. I have no idea how I could mock, stub or override SecuredAction and UserAwareAction objects from securesocial. Still I don't want to convert my tests into route(...) tests. They are intended to test only the controller's behavior.
Have someone encountered the same problem? May be there are any hints how it could be solved?
PS: Play framework 2.2.1, securesocial - 2.1.2
It seem like the author of the code really hasn't emphasized testability, which has forced users to come up with their own novel solutions. This one by user jeantil could be helpful:
class FakeAuthenticatorStore(app:Application) extends AuthenticatorStore(app) {
var authenticator:Option[Authenticator] = None
def save(authenticator: Authenticator): Either[Error, Unit] = {
this.authenticator=Some(authenticator)
Right()
}
def find(id: String): Either[Error, Option[Authenticator]] = {
Some(authenticator.filter(_.id == id)).toRight(new Error("no such authenticator"))
}
def delete(id: String): Either[Error, Unit] = {
this.authenticator=None
Right()
}
}
abstract class WithLoggedUser(val user:User,override val app: FakeApplication = FakeApplication()) extends WithApplication(app) with Mockito{
lazy val mockUserService=mock[UserService]
val identity=IdentityUser(Defaults.googleId, user)
import helpers._
import TestUsers._
def cookie=Authenticator.create(identity) match {
case Right(authenticator) => authenticator.toCookie
}
override def around[T: AsResult](t: =>T): execute.Result = super.around {
mockUserService.find(Defaults.googleId) returns Some(identity)
UserService.setService(mockUserService)
t
}
}
val excludedPlugins=List(
,"service.login.MongoUserService"
,"securesocial.core.DefaultAuthenticatorStore"
)
val includedPlugins = List(
"helpers.FakeAuthenticatorStore"
)
def minimalApp = FakeApplication(withGlobal =minimalGlobal, withoutPlugins=excludedPlugins,additionalPlugins = includedPlugins)
which then allows testing like this
"create a new user password " in new WithLoggedUser(socialUser,minimalApp) {
val controller = new TestController
val req: Request[AnyContent] = FakeRequest().
withHeaders((HeaderNames.CONTENT_TYPE, "application/x-www-form-urlencoded")).
withCookies(cookie) // Fake cookie from the WithloggedUser trait
val requestBody = Enumerator("password=foobarkix".getBytes) andThen Enumerator.eof
val result = requestBody |>>> controller.create.apply(req)
val actual: Int= status(result)
actual must be equalTo 201
}
After some thinking, probing and experimenting I've ended up with an elegant solution. The solution relies on "cake pattern" of dependency injection. Like this:
Code in controller:
trait AbstractSecurity {
def Secured(action: SecuredRequest[AnyContent] => Result): Action[AnyContent]
}
trait SecureSocialSecurity extends AbstractSecurity with securesocial.core.SecureSocial {
def Secured(action: SecuredRequest[AnyContent] => Result): Action[AnyContent] = SecuredAction { action }
}
abstract class MyController extends Controller with AbstractSecurity {
def entityDao: IEntityDao
def list = Secured { request =>
Ok(
JsArray(entityDao.list())
)
}
}
object MyController extends MyController with PsqlDaoProvider with SecureSocialSecurity
And test code:
trait MockedSecurity extends AbstractSecurity {
val user = Account(NotAssigned, IdentityId("test", "userpass"), "Test", "User",
"Test user", Some("test#user.com"), AuthenticationMethod("userPassword"))
def Secured(action: SecuredRequest[AnyContent] => play.api.mvc.Result): Action[AnyContent] = Action { request =>
action(new SecuredRequest(user, request))
}
}
val controller = new MyController with MockDaoProvider with MockedSecurity
"IssueController.list" should {
"return an OK" in {
entityDaoMock.list().returns(List())
val result = controller.list()(FakeRequest())
status(result) must equalTo(OK)
}
}
Still there is a drawback - the tests depends on securesocial classes as well... but... is it really a drawback?
I don't know how this approach will work in more complex situations, we'll see.

how to unit test grails' message tag

in the controller there is an action:
def delete = {
withDomain {
it.delete()
flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'chocolateBar.label', default: 'ChocolateBar'), it.name])}"
redirect action: 'list'
}
}
which can be tested in development. while in unit test, the message(..) method throws exception ( groovy.lang.MissingMethodException: No signature of method: longtest.ChocolateBarController.message() is applicable for argument types: (java.util.LinkedHashMap) values: [[code:chocolateBar.label, default:ChocolateBar]]):
public void testDelete() {
controller.params.id = '3'
controller.delete()
assert 'list'==controller.redirectArgs.action
}
After study, a mockTagLib method should be called during setup. But found no correct class name for built-in message(..). Please help.
I've solved the problem in unit controller test. like this:
//This is inside Spock test
#Shared
ResourceBundleMessageSource messageSource = null
#Shared
Closure mockMessage = {Map map ->
return messageSource.getMessage((String)map.code, (Object[])map.args, Locale.default)
}
def setupSpec(){
URL url = new File('grails-app/i18n').toURI().toURL()
messageSource = new ResourceBundleMessageSource()
messageSource.bundleClassLoader = new URLClassLoader(url)
messageSource.basename = 'messages'
messageSource.setDefaultEncoding("utf-8")
}
def setup(){
controller.metaClass.message = mockMessage
}
This code is for spock test, but main idea is also available for normal grails test.
In running phase(not test),
calling "message" in controller class results in calling "message" of ValidationTagLib class,
but they are not bind in unit test phase.
So I made almost same logic of "message" of ValidationTagLib,
and bind it(named "mockMessage") to controller.message.
With this code, you can execute "message" correctly in controller class in test.

Unit Test Grails with Service

I'm trying to do a test on controller that uses 3 Domains and a service to perform persistence, when I use these values ​​in view he is saving normally, but in my unit test does not pass validation, I do not understand why . If someone who has been there can help me, I do not know if the Mock I'm doing is correct, I followed the examples in the documentation oficial .
thats the error message:
junit.framework.AssertionFailedError: expected:<1> but was:<0>
Thats my code for Test:
#TestFor(UsuarioController)
#Mock([SecRole, UsuarioService, Usuario, Cliente, Secuser])
#TestMixin(ControllerUnitTestMixin)
class UsuarioTests {
private def usuarioCommand
private def service
#Before
void setUp() {
usuarioCommand = mockCommandObject(UsuarioCommand)
service = mockFor(UsuarioService)
}
#Test
void testCadastrarUsuarioCorreto() {
usuarioCommand.perfil = 2
usuarioCommand.nome = "Michael Swaltz"
usuarioCommand.cpf = "381.453.718-13"
usuarioCommand.email = "michael.s#mail.com"
usuarioCommand.login = "login"
usuarioCommand.senha = "senha"
usuarioCommand.senhaRepeat = "senha"
assertTrue( usuarioCommand.validate() );
controller.usuarioService = service
controller.create(usuarioCommand)
assertEquals(1, Usuario.count())
}
This is my controller action:
def create = { UsuarioCommand usuario ->
if(!params.create) return
if(!usuario.hasErrors()) {
def secuser = new Secuser(username: usuario.login, password: usuario.senha, senha: usuario.senhaRepeat, enabled: true)
def user = new Usuario(nomeUsuario: usuario.nome, email: usuario.email, cpf: usuario.cpf, idNivelAcesso: usuario.perfil)
def cliente = Cliente.findByUsuario( session.usuario )
user.setIdCliente(cliente)
def secrole = SecRole.get( usuario.perfil )
try{
usuarioService.save(user, secuser, secrole)
flash.message = "Usuário ${usuario.nome} cadastrado.".toString()
redirect (action: 'list')
}catch(ValidationException ex) {
StringBuilder mensagem = new StringBuilder();
ex.errors.fieldErrors.each { FieldError field ->
mensagem.append("O campo ").append( field.field )
.append(" da classe ")
.append( field.objectName )
.append(" com o valor ")
.append( field.rejectedValue )
.append(" não passou na validação.")
.append("\n")
}
flash.error = mensagem.toString()
return [usr: usuario]
}catch(ex) {
flash.error = ex.message
render "Erro"
//return [usr: usuario]
}
}else {
usuario.errors.allErrors.each { println it }
render "Erro"
//return [usr: usuario]
}
}
mockFor would give you back a mock control. You have to explicitly call createMock() on the mock control to get the actual mocked object.
service = mockFor(UsuarioService).createMock()
Have a look at "Mocking Collaborators" from the same link you referred. The test can be optimized if you still face an issue.
similar example and one here.
You need to set an expectation on the UsarioService to say what will be returned when usarioService.save(...) is called.
Before getting to that point, you need to say in the test
mockUsarioService.createMock() which will create the actual instance of the mock object, that's what you will pass to the controller usarioService attribute. Copied the code below from the Grails documenation. http://grails.org/doc/1.1/guide/9.%20Testing.html
String testId = "NH-12347686"
def otherControl = mockFor(OtherService)
otherControl.demand.newIdentifier(1..1) {-> return testId }
// Initialise the service and test the target method.
def testService = new MyService()
testService.otherService = otherControl.createMock()