I have a command object for registering user, and I want to check how old is the user. This command object has a service dependency. How can I test custom validator for my dateOfBirth property? As it looks now is taken straight from documentation, here.
class RegisterUserCommand {
def someService
String username
String password
String password2
String email
Date dateOfBirth
static constraints = {
// other constraints
dateOfBirth blank: false, validator: {val, obj ->
return obj.someService.calculateAge(val) >= 18
}
}
So basically the question is: how can I mock 'obj' parameter of the validator closure?
The easiest way to test validation on a command object is to use GrailsUnitTestCase.mockForConstraintsTests. A mock validate method will be applied to your command object, and you can just call validate() like you would outside of a test.
Here's an example of how you could write your unit test. The blank constraint isn't meaningful for dates, so I've changed it to nullable: false.
import grails.test.GrailsUnitTestCase
class RegisterUserCommandTests extends GrailsUnitTestCase {
RegisterUserCommand cmd
protected void setUp() {
super.setUp()
cmd = new RegisterUserCommand()
mockForConstraintsTests RegisterUserCommand, [cmd]
}
void testConstraintsNull() {
cmd.dateOfBirth = null
cmd.someService = [calculateAge: { dob -> 18 }]
def result = cmd.validate()
assert result == false
assert cmd.errors.getFieldErrors('dateOfBirth').code == ['nullable']
}
void testConstraintsCustom() {
cmd.dateOfBirth = new Date()
cmd.someService = [calculateAge: { dob -> 17 }]
def result = cmd.validate()
assert result == false
assert cmd.errors.getFieldErrors('dateOfBirth').code == ['validator.invalid']
}
}
Note that your service won't get injected in a unit test (it will in an integration test though), so you'll either need to mock it, as above, or create an instance and assign it to cmd.someservice.
Related
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!
Do i need to mock interfaces that does not call, for instance user name and password field is empty? I'm trying to write test first but confused if mocks should be used.
My login test
private val authRepository: AuthRepository = mockk()
private val userManager: AccountManager = mockk()
private lateinit var authUseCase: AuthUseCase
#BeforeEach
fun setUp() {
clearMocks(authRepository)
clearMocks(userManager)
authUseCase = AuthUseCase(authRepository, userManager)
}
/**
* Scenario: Login check with empty fields:
* * Given I am on the login page
* * When I enter empty username
* And I enter empty password
* And I click on the "Login" button
* * Then I get empty fields error.
*/
#Test
fun `Empty fields result empty fields error`() {
// Given
// When
val expected = authUseCase.login("", "", false)
// Then
verify(exactly = 0) {
authRepository.login(or(any(), ""), or(any(), ""), any())
}
expected assertEquals EMPTY_FIELD_ERROR
}
Do i have to mock interface for the given part of the test or AccountManager even though they are not called since user name and/or fields are empty?
This is the final version of login method i intend to write after tests
class AuthUseCase(
private val authRepository: AuthRepository,
private val accountManager: AccountManager
) {
private var loginAttempt = 1
/*
STEP 1: Throw exception for test to compile and fail
*/
// fun login(
// userName: String,
// password: String,
// rememberMe: Boolean = false
// ): AuthenticationState {
// throw NullPointerException()
// }
/*
STEP3: Check if username or password is empty
*/
// fun login(
// userName: String,
// password: String,
// rememberMe: Boolean = false
// ): AuthenticationState {
//
//
// if (userName.isNullOrBlank() || password.isNullOrBlank()) {
// return EMPTY_FIELD_ERROR
// }else {
// throw NullPointerException()
// }
//
// }
/**
* This is the final and complete version of the method.
*/
fun login(
userName: String,
password: String,
rememberMe: Boolean
): AuthenticationState {
return if (loginAttempt >= MAX_LOGIN_ATTEMPT) {
MAX_NUMBER_OF_ATTEMPTS_ERROR
} else if (userName.isNullOrBlank() || password.isNullOrBlank()) {
EMPTY_FIELD_ERROR
} else if (!checkUserNameIsValid(userName) || !checkIfPasswordIsValid(password)) {
INVALID_FIELD_ERROR
} else {
// Concurrent Authentication via mock that returns AUTHENTICATED, or FAILED_AUTHENTICATION
val authenticationPass =
getAccountResponse(userName, password, rememberMe)
return if (authenticationPass) {
loginAttempt = 0
AUTHENTICATED
} else {
loginAttempt++
FAILED_AUTHENTICATION
}
}
}
private fun getAccountResponse(
userName: String,
password: String,
rememberMe: Boolean
): Boolean {
val authResponse =
authRepository.login(userName, password, rememberMe)
val authenticationPass = authResponse?.authenticated ?: false
authResponse?.token?.let {
accountManager.saveToken(it)
}
return authenticationPass
}
private fun checkUserNameIsValid(field: String): Boolean {
return field.length >15 && field.endsWith("#example.com")
}
private fun checkIfPasswordIsValid(field: String): Boolean {
return field.length in 6..10
}
}
Should i only mock when all other states and passed i get a mock response from repository and interaction with account manager occurs?
What should be given section of the test?
Edit:
I updated given section of this test to
#Test
fun `Empty fields result empty fields error`() {
// Given
every { authRepository.login(or(any(), ""), or(any(), "")) } returns null
// When
val expected = authUseCase.login("", "", false)
// Then
verify(exactly = 0) { authRepository.login(or(any(), ""), or(any(), "")) }
expected assertThatEquals EMPTY_FIELD_ERROR
}
Is there something wrong with this kind of behavior testing?
I would suggest that you don't need the verify in the "Empty fields result empty fields error" test. I would also suggest you write separate tests for each empty field. If you were doing strict TDD you would be testing each condition as you wrote the code. i.e.
'Empty username should error" would be the first test and the first condition tested, then "Empty password should error" the next (after you have done two separate written your second test your code may look like
if (userName.isNullOrBlank()) {
return EMPTY_FIELD_ERROR
}
if (password.isNullOrBlank() {
return EMPTY_FIELD_ERROR
}
Once both the tests above pass you could refactor to
if (userName.isNullOrBlank() || password.isNullOrBlank()) {
EMPTY_FIELD_ERROR
}
Once you start testing the conditional statements for checkUserNameIsValid and checkIfPasswordIsValid, you would need to introduce the authRepository and accountManager to your class (constructor injection) and then you would need to start mocking the calls as you use them. Generally mocking frameworks will fake an object (i.e. the code will run but won't return any meaningful result). You should aim to return actual mock data when you want to test specific behavior i.e. you should be returning a valid object from the authRepository.login when you are testing for a successful login. Generally I stay away from using setup methods in the #BeforeEach and use either a factory method or builder to create my class under test. I am unfamiliar with the kotlin syntax so can at best do some sudo code to demonstrate how your builder or factory functions may look like.
// overloaded factory function
fun create() {
val authRepository: AuthRepository = mockk()
val userManager: AccountManager = mockk()
return AuthUseCase(authRepository, userManager);
}
fun create(authRepository: AuthRepository) {
val userManager: AccountManager = mockk()
return AuthUseCase(authRepository, userManager);
}
fun create(authRepository: AuthRepository, userManager: AccountManager) {
return AuthUseCase(authRepository, userManager);
}
You will need to have a look at how to create a builder in kotlin but the end result you would be looking for is that the builder always starts setting the dependencies for you class under test as mocks that do nothing but allows you to change those mocks.
e.g.
AuthUseCase authUseCase = AuthUseCaseBuilder.Create().WithAuthRepository(myMockAuthRepository).Build();
One final thing. I purposely left out discussing loginAttempt check above as to me it looks like the AuthUseCase is a service class that will be used by multiple users and live for the lifetime of the request in which case you don't want to maintain state within the class (i.e. the loginAttempt variable has the same lifetime as the class). It would be better to record the attempts per username in a database table and the attempt count would need to be reset after each successful login.
Hope this helps.
I have the following Domain class with derived property lowercaseTag.
class Hashtag {
String tag
String lowercaseTag
static mapping = {
lowercaseTag formula: 'lower(tag)'
}
}
If I run the following unit test, it will fail on the last line, because lowercaseTag property is null and by default all properties have nullable: false constraint.
#TestFor(Hashtag)
class HashtagSpec extends Specification {
void "Test that hashtag can not be null"() {
when: 'the hashtag is null'
def p = new Hashtag(tag: null)
then: 'validation should fail'
!p.validate()
when: 'the hashtag is not null'
p = new Hashtag(tag: 'notNullHashtag')
then: 'validation should pass'
p.validate()
}
}
The question is how to properly write unit tests in such cases? Thanks!
As I'm sure you've figured out, the lowercaseTag cannot be tested because it's database dependent; Grails unit tests do not use a database, so the formula/expression is not evaluated.
I think the best option is to modify the constraints so that lowercaseTag is nullable.
class Hashtag {
String tag
String lowercaseTag
static mapping = {
lowercaseTag formula: 'lower(tag)'
}
static constraints = {
lowercaseTag nullable: true
}
}
Otherwise, you'll have to modify the test to force lowercaseTag to contain some value so that validate() works.
p = new Hashtag(tag: 'notNullHashtag', lowercaseTag: 'foo')
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)
}
}
...
}
I have a controller that uses a command object in a controller action. When mocking this command object in a grails' controller unit test, the hasErrors() method always returns false, even when I am purposefully violating its constraints. The more baffling thing is that in production, hasErrors() works! So this is just a testing problem.
def save = { RegistrationForm form ->
if(form.hasErrors()) {
// code block never gets executed
} else {
// code block always gets executed
}
}
In the test itself, I do this:
mockCommandObject(RegistrationForm)
def form = new RegistrationForm(emailAddress: "ken.bad#gmail",
password: "secret", confirmPassword: "wrong")
controller.save(form)
I am purposefully giving it a bad email address, and I am making sure the password and the confirmPassword properties are different. In this case, hasErrors() should return true... but it doesn't. I don't know how my testing can be any where reliable if such a basic thing does not work :/
Here is the RegistrationForm class, so you can see the constraints I am using:
class RegistrationForm {
def springSecurityService
String emailAddress
String password
String confirmPassword
String getEncryptedPassword() {
springSecurityService.encodePassword(password)
}
static constraints = {
emailAddress(blank: false, email: true)
password(blank: false, minSize:4, maxSize: 10)
confirmPassword(blank: false, validator: { confirmPassword, form ->
confirmPassword == form.password
})
}
}
Have you tried mockForConstraintsTests ?
E.g. something like...
void testSomething() {
mockForConstraintsTests(RegistrationForm)
def form = new RegistrationForm(emailAddress: "ken.bad#gmail", password: "secret", confirmPassword: "wrong")
form.validate()
assert 1 == form.errors.getErrorCount()
}
Try just testing the RegistrationForm command object first in its own unit test. (gain some confidence that its actually working)
Maybe using the above for the basis of your test will help!?!?