I'm stuck with a unit test and fairly new to testing.
I tried to create a test for a class with toMap/fromMap methods and write tests for it.
Within the class I have the following code, where I added the hashCode and == operator methods to prepare the class for the test.
I have the exact same setup for other classes, where the test works..
String uid;
String email;
String userName;
String diagnose;
bool surgery;
List<Goal> goals;
List<KOS> kos;
UserCase({
this.uid,
this.email,
this.userName,
this.diagnose,
this.surgery = false,
this.goals,
this.kos,
});
factory UserCase.fromData(Map<String, dynamic> data) {
if (data == null) {
print('UserCase fromData NULL');
return null;
}
final String uid = data['uid'] ?? '';
final String email = data['email'] ?? '';
final String userName = data['userName'] ?? '';
final String diagnose = data['diagnose'] ?? '';
final bool surgery = data['surgery'] ?? false;
final List<Goal> goals = data['goals'] == null
? []
: List.from(data['goals'].map((e) => Goal.fromData(e)));
final List<KOS> kos = data['kos'] == null
? []
: List.from(data['kos'].map((e) => KOS.fromData(e)));
return UserCase(
uid: uid,
email: email,
userName: userName,
diagnose: diagnose,
surgery: surgery,
goals: goals,
kos: kos,
);
}
Map<String, dynamic> toMap() {
return {
'uid': uid,
'email': email,
'userName': userName,
'diagnose': diagnose,
'surgery': surgery,
'goals': goals == null || goals.isEmpty
? []
: goals.map((e) => e.toMap()).toList(),
'kos':
kos == null || kos.isEmpty ? [] : kos.map((e) => e.toMap()).toList(),
};
}
**UPDATE: adding hashList to ListObjects**
#override
int get hashCode => hashValues(
uid, email, userName, diagnose, surgery, hashList(goals), hashList(kos));
old:
*#override
int get hashCode {
return hashValues(uid, email, userName, diagnose, surgery, goals, kos);
}*
#override
bool operator ==(other) {
if (identical(this, other)) return true;
if (runtimeType != other.runtimeType) return false;
final UserCase otherCase = other;
return uid == otherCase.uid &&
email == otherCase.email &&
userName == otherCase.userName &&
diagnose == otherCase.diagnose &&
surgery == otherCase.surgery &&
goals == otherCase.goals &&
kos == otherCase.kos;
}
}
And this is the test that fails:
final userCase = UserCase.fromData({
'uid': 'id123',
'email': 'email123',
'userName': 'username',
'diagnose': 'ACL',
'surgery': true,
'goals': [],
'kos': [],
});
expect(
userCase,
UserCase(
uid: 'id123',
email: 'email123',
userName: 'username',
diagnose: 'ACL',
surgery: true,
goals: [],
kos: [],
));
});
**UPDATED ERROR MESSAGE after hashList has been added **
test/usercase_test.dart: fromData case with all properties [E]
Expected: UserCase:<uid: id123, email: email123, userName: username, diagnose: ACL, surgery: true, goals: [], kos: []>
Actual: UserCase:<uid: id123, email: email123, userName: username, diagnose: ACL, surgery: true, goals: [], kos: []>
package:test_api expect
package:flutter_test/src/widget_tester.dart 431:3 expect
usercase_test.dart 21:7 main.<fn>.<fn>```
And that is the error message - this one changed now to the one above, after adding the hashList method to the list properties.
.../test/usercase_test.dart: fromData case with all properties [E]
'dart:ui/hash_codes.dart': Failed assertion: line 17: '<optimized out>': is not true.
dart:ui hashValues
../lib/Classes/UserCase.dart 71:12 UserCase.hashCode
dart:collection _CompactLinkedHashSet.contains
package:matcher/src/pretty_print.dart 28:14 prettyPrint._prettyPrint
package:matcher/src/pretty_print.dart 119:22 prettyPrint
package:matcher/src/description.dart 49:11 StringDescription.addDescriptionOf
package:matcher/src/equals_matcher.dart 267:19 _DeepMatcher.describe
package:matcher/src/description.dart 47:13 StringDescription.addDescriptionOf
package:test_api expect
package:flutter_test/src/widget_tester.dart 431:3 expect
usercase_test.dart 22:7 main.<fn>.<fn>ยดยดยด
I can't see anything obvious suggesting which property is causing the test to fail.
A couple of suggestions:
Try removing one property at a time from the comparisons in the == implementation.
Try using the Equatable package for your model class. This reduces the boilerplate a lot and is less error-prone than implementing == and hashCode by hand.
I have a field in a domain class that I applied the blank: false constraint to and I write a unit test to verify that a spaces only string for the field doesn't pass the validation, but the test fails:
void 'test name cannot be blank'() {
when:
domain.name = ' '
then:
!domain.validate(['name'])
domain.errors['name'].code == 'blank'
}
I this the ConditionNotSatisfiedError error on the line !domain.validate(['name']): ConditionNotSatisfiedError
This is my domain object:
class Thing {
String name
static constraints = {
name nullable: false, blank: false, size: 1..20
}
}
You haven't indicate what version of Grails you are using and that will likely be relevant. The project at https://github.com/jeffbrown/selenablank demonstrates how this works in 3.3.3.
https://github.com/jeffbrown/selenablank/blob/master/grails-app/domain/selenablank/Thing.groovy
package selenablank
class Thing {
String name
static constraints = {
name nullable: false, blank: false, size: 1..20
}
}
https://github.com/jeffbrown/selenablank/blob/master/src/test/groovy/selenablank/ThingSpec.groovy
package selenablank
import grails.testing.gorm.DomainUnitTest
import spock.lang.Specification
class ThingSpec extends Specification implements DomainUnitTest<Thing> {
void 'test name cannot be blank'() {
when:
domain.name = ' '
then:
!domain.validate(['name'])
domain.errors['name'].code == 'blank'
}
}
I am trying to perform basic unit test on a Grails domain class.
Here is the domain class:
class User {
String username
String password
String email
static constraints = {
username size: 4..15, blank: false, unique: true
password size: 5..15, password: true, blank: false
email email: true, blank: false
}
}
Here is the unit test class:
#TestFor(User)
class UserTests {
void testCreateUser() {
def u = new User(username:"ab")
assertFalse "There should be errors", u.validate()
assertTrue "Should be errors here", u.hasErrors()
}
}
username is constrained by size from 4 to 15. However, when I run grails test-app the above test succeeds. I don't understand why the constraint isn't causing it to fail.
You didn't write which Grails version you use, but generally you should set up User class to be tested for constraint checks. Add this to your UserTests
def setUp() {
mockForConstraintsTests(User)
}
It seems docs for mongodb-1.1.0GA are outdated when it comes to unit testing section: http://springsource.github.com/grails-data-mapping/mongo/manual/ref/Testing/DatastoreUnitTestMixin.html
Following code
#TestFor(Employee)
class EmployeeTests extends GroovyTestCase {
void setUp() {
}
void tearDown() {
}
void testSomething() {
mockDomain(Employee)
def s = new Employee(firstName: "first name", lastName: "last Name", occupation: "whatever")
s['testField'] = "testValue"
s.save()
assert s.id != null
s = Employee.get(s.id)
assert s != null
assert s.firstName == "first name"
assert s['testField'] == "testValue"
}
}
fails with this error:
No such property: testField for class: Employee
Employee class is pretty straightforward:
class Employee {
String firstName
String lastName
String occupation
static constraints = {
firstName blank: false, nullable: false
lastName blank: false, nullable: false
occupation blank: false, nullable: false
}
}
So, is unit testing of dynamic attributes possible? If it is, how?
There's no out of the box support for dynamic attributes but it's fairly easy to add. I've put the following code in my setup method. It will add dynamic attributes to any domain classes you have enabled using #TestFor or #Mock.
grailsApplication.domainClasses.each { domainClass ->
domainClass.metaClass.with {
dynamicAttributes = [:]
propertyMissing = { String name ->
delegate.dynamicAttributes[name]
}
propertyMissing = { String name, value ->
delegate.dynamicAttributes[name] = value
}
}
}
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.