I'm using Grails 2.4.1 and having trouble understanding how to properly test a unique constraint on a domain class.
My domain class looks like:
class Person {
String name
static constraints = {
name( unique: true, blank: false )
}
}
and my test:
#TestFor(Person)
#TestMixin(GrailsUnitTestMixin)
class PersonSpec extends Specification {
void "name should be unique"() {
given:
mockForConstraintsTests Person
when:
def one = new Person( name: 'john' )
then:
one.save()
when:
def two = new Person( name: 'john' )
then:
! two.validate()
two.errors.hasFieldErrors( 'name' )
}
The second instance is validating despite apparently violating the unique constraint. Can someone please explain what's going on here and how I should best correct it?
Thanks!
--john
I do not think it is the best approach to test the constraints by triggering them. Generally we want to test that our code is working and not that Grails is validating the constraints.
Why test the framework (i.e. Grails)? Hopefully the Grails people have done that already :-)
(yes, there are situations were it makes sense to check the framework, but I don't think this is one).
So the only thing to test is that we have placed the correct constraints on the domain class:
#TestFor(Person)
class PersonSpec extends Specification {
void "name should have unique/blank constraints"() {
expect:
Person.constraints.name.getAppliedConstraint('unique').parameter
! Person.constraints.name.getAppliedConstraint('blank').blank
}
}
If it is worth writing that test is another question...
This is exactly kind of mistake I used to do as a beginner. Testing grails stuff whether it is working as expected or not. Testing framework related stuff not a good idea. It means then testing all constraints, predefined methods and even save(), update() etc. So it would mean testing grails framework again rather than developing your application.
Testing should generally consists of testing your business logic related stuff.
Related
Usually I was ending up writing test cases for a Domain by writing them for constraints and any custom methods(created by us in application) as we know we shouldn't test obvious.
But the time we started using coverage plugin, we found that our domains line of code is not fully covered which was due to gorm hooks(onInsert, beforeUpdate) that we never wrote test cases for.
Is there a way we can test these. One possible way that seems obvious but not suitable is to call another method(containing all code which was earlier in hooks) within these hooks and test that method only and be carefree for hooks.
Any solutions...
Edit
Sample code in domain that I want to unit-test:
class TestDomain{
String activationDate
def beforeInsert() {
this.activationDate = (this.activationDate) ?: new Date()//first login date would come here though
encodePassword()
}
}
How can I unit-test beforeInsert or I would end up writing integration test case?
Perhaps a unit test like:
import grails.test.mixin.TestFor
#TestFor(TestDomain)
class TestDomainSpec extends Specification {
def "test beforeSave"() {
given:
mockForConstraintsTests(TestDomain)
when:
def testDomain = new TestDomain().save(flush:true)
then:
testDomain.activationDate != null
}
}
I've just started with unit testing in CakePHP (yay!) and ran into the following challenge. Hope somebody can help me :-)
Situation
My model uses a Behavior to send changes to an API after saving it locally. I would like to fake all calls made to the API during the test (those will be tested seperately) to save load on the API server, and more important, not actually save the changes :-)
I'm using CakePHP 2.4.1.
What I've tried
Read the docs. The manual shows how to do this for Components and Helpers but not for Behaviors.
Google. What I've found:
A Google Group post which says it simply "isn't possible". I don't take no for an answer.
An article explaining how to mock an object. Comes pretty close.
The code from the article reads:
$provider = $this->getMock('OurProvider', array('getInfo'));
$provider->expects($this->any())
->method('getInfo')
->will($this->returnValue('200'));
It might be the wrong direction, but I think that might be a good start.
What I want
Effectively: A snippet of code to demo how to mock a behavior in a CakePHP Model for unit testing purposes.
Maybe this question will result in an addition of the CakePHP manual too as an added bonus, since I feel it's missing in there.
Thanks in advance for the effort!
Update (2013-11-07)
I've found this related question, which should answer this question (partly). No need to mock up the API, instead I can create a Behavior test that the model will use.
I'm trying to figure out what that BehaviorTest should look like.
Use the class registry
As with many classes, behaviors are added to the class registry using the class name as the key, and for subsequent requests for the same object loaded from the classregistry. Therefore, the way to mock a behavior is simply to put it in the class registry before using it.
Full Example:
<?php
App::uses('AppModel', 'Model');
class Example extends AppModel {
}
class TestBehavior extends ModelBehavior {
public function foo() {
throw new \Exception('Real method called');
}
}
class BehaviorExampleTest extends CakeTestCase {
/**
* testNormalBehavior
*
* #expectedException Exception
* #expectedExceptionMessage Real method called
* #return void
*/
public function testNormalBehavior() {
$model = ClassRegistry::init('Example');
$model->Behaviors->attach('Test');
$this->assertInstanceOf('TestBehavior', $model->Behaviors->Test);
$this->assertSame('TestBehavior', get_class($model->Behaviors->Test));
$this->assertSame(['foo' => ['Test', 'foo']], $model->Behaviors->methods());
$model->foo();
}
public function testMockedBehavior() {
$mockedBehavior = $this->getMock('TestBehavior', ['foo', 'bar']);
ClassRegistry::addObject('TestBehavior', $mockedBehavior);
$model = ClassRegistry::init('Example');
$model->Behaviors->attach('Test');
$this->assertInstanceOf('TestBehavior', $model->Behaviors->Test);
$this->assertNotSame('TestBehavior', get_class($model->Behaviors->Test));
$expected = [
'foo' => ['Test', 'foo'],
'bar' => ['Test', 'bar'],
'expects' => ['Test', 'expects'], // noise, due to being a mock
'staticExpects' => ['Test', 'staticExpects'], // noise, due to being a mock
];
$this->assertSame($expected, $model->Behaviors->methods());
$model->foo(); // no exception thrown
$mockedBehavior
->expects($this->once())
->method('bar')
->will($this->returnValue('something special'));
$return = $model->bar();
$this->assertSame('something special', $return);
}
}
I'm sure this is a fairly common situation. I'm using the Spring Security Core plugin and want to create a domain model that has a Person limited to certain roles:
class Workgroup {
Person manager
...
static constraints = {
manager(validator: {mgr ->
// it feels like there should be a more elegant, groovy way of doing this.
def auths = mgr.getAuthorities();
def returny = false
auths.each {
if(it.authority == 'ROLE_MANAGER')
{
returny = true
}
}
return returny
})
}
}
This test fails like a mofo:
void testInvalidManager() {
def nick = new Person(username:'Nick')
def nonManagerRole = new Role(authority:'ROLE_EMPLOYEE')
UserRole.create(nick,nonManagerRole)
def awesome = new Workgroup(name:'mooCows', manager:nick)
mockForConstraintsTests(Workgroup, [awesome])
assertFalse awesome.validate()
assertEquals "validator", awesome.errors["manager"]
}
testInvalidManager Error No signature of method: users.UserRole.save() is applicable for argument types: (java.util.LinkedHashMap) values: [[flush:false, insert:true]] Possible solutions: wait(), any(), wait(long), use([Ljava.lang.Object;), isCase(java.lang.Object), each(groovy.lang.Closure)
groovy.lang.MissingMethodException: No signature of method: users.UserRole.save() is applicable for argument types: (java.util.LinkedHashMap) values: [[flush:false, insert:true]]
Possible solutions: wait(), any(), wait(long), use([Ljava.lang.Object;), isCase(java.lang.Object), each(groovy.lang.Closure)
at users.UserRole.create(UserRole.groovy:32)
at users.UserRole.create(UserRole.groovy)
at users.UserRole$create.call(Unknown Source)
at users.WorkgroupTests.testInvalidManager(WorkgroupTests.groovy:17)
Is this better covered in Integration than Unit Testing? Do I need to mock UserRole (if so, how?)? How are these types of tests normally done?
UserRole.create() calls save(), so you need to use mockDomain() instead of just mockForConstraintsTests().
But that's only if you're ok with testing the domain model with mocks, which I would never do. The mocking support in Grails should be used when testing Controllers or other classes that use domain classes but shouldn't be bothered with real persistence, creating a database (even in-memory), etc. By removing that dependency you're concentrating on the current tier, trusting that the other tier is already properly tested. But when you use mocking to test domain classes, you're really just testing the mocking framework. So I always use integration tests for domain classes so they run against a real database.
To answer the implicit question from your code example, I'd write the constraint as
static constraints = {
manager validator: { mgr ->
mgr.authorities.find { it.authority == 'ROLE_MANAGER' } != null
}
}
The issue with its bulk is that you're using each() when a regular for loop would be preferable since you can return from a for loop. Use each() only when you really want to invoke the closure on every instance. Here's one that's less groovy than the other one but uses a for loop:
static constraints = {
manager validator: { mgr ->
for (auth in mgr.getAuthorities()) {
if (it.authority == 'ROLE_MANAGER') {
return true
}
}
return false
}
}
Is there a less bloated way to test constraints? It seems to me that this is too much code to test constraints.
class BlogPostTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
mockDomain BlogPost
}
void testConstraints() {
BlogPost blogPost = new BlogPost(title: "", text: "")
assertFalse blogPost.validate()
assertEquals 2, blogPost.errors.getErrorCount()
assertEquals "blank", blogPost.errors.getFieldError("title").getCode()
assertEquals "blank", blogPost.errors.getFieldError("text").getCode()
blogPost = new BlogPost(title: "title", text: ObjectMother.bigText(2001))
assertFalse blogPost.validate()
assertEquals 1, blogPost.errors.getErrorCount()
assertEquals "maxSize.exceeded", blogPost.errors.getFieldError("text").getCode()
}
}
I'd advise against testing getErrorCount(), as you'll make your tests fragile (as you add other constraints, you'll have to remember to update every instance of new BlogPost() anywhere in your test cases). Just check hasErrors().
Other than that... for each constraint, you need to generate some test data that violates it, call the validation routine, and assert on the errors. This is the code you need.
Refactor out some methods to remove the duplication. example:
private void assertConstraintWorks(clazz, fieldName, testData, expectedErrorCode) {
def instance = clazz.newInstance((fieldName): testData)
assertFalse instance.validate()
assertTrue instance.hasErrors()
assertEquals expectedErrorCode, instance.errors?.getFieldError(fieldName)?.code
}
void testConstraints() {
assertConstraintWorks BlogPost, 'title', '', 'blank'
assertConstraintWorks BlogPost, 'text', '', 'blank'
assertConstraintWorks BlogPost, 'text', ObjectMother.bigText(2001), 'maxSize.exceeded'
}
Ross Niemi, a coworker of mine, created the Domain Expectations plugin which uses a nice DSL for exercising and validating constraints on grails domain objects. We use it at my work place, and I've been very happy with it.
Maybe not the answer you want to hear, but: Do you really want to test the constraints defined in your domain model? In essence, what you are doing here is testing the validation framework of Grails rather than your own code. Don't get me wrong, tests are necessary (especially with dynamic languages like Groovy), but IMHO one shouldn't just blindly test everything that comes across. Maybe I'm just not too much of an hardcore TDD guy, but I just don't see the point here.
This blog post has a good description of unit testing constraints. One thing I didn't see above is the use of mockForConstraintsTests() (which eliminates need for mockDomain())
And to comment on Daniel, yes I would test the constraints defined in my domain model to assert that I have correctly constrained the domain model. You make a typo in a constraint declaration and you won't find it until an integration or functional test (and in the meantime will drive you nuts)
The test class below verifies that a simple HttpService gets content from a given URL. Both the implementations shown make the test pass, though one is clearly wrong because it constructs the URL with an incorrect argument.
To avoid this and correctly specify the behaviour I want, I'd like to verify that in the use block of the test case, I construct one (and only one) instance of the URL class, and that the url argument to the constructor is correct. A Groovy enhancement seems like it would let me add the statement
mockURLContext.demand.URL { assertEquals "http://www.foo.com", url }
but what can I do without that Groovy enhancement?
Update: Replaced "mock" with "stub" in the title, as I'm only interested in checking the state not necessarily the detail of the interactions. Groovy has a StubFor mechanism that I haven't used, so I'll leave my code as is, but I think you could just replace MockFor with StubFor throughout.
import grails.test.*
import groovy.mock.interceptor.MockFor
class HttpServiceTests extends GrailsUnitTestCase {
void testGetsContentForURL() {
def content = [text : "<html><body>Hello, world</body></html>"]
def mockURLContext = new MockFor(URL.class)
mockURLContext.demand.getContent { content }
mockURLContext.use {
def httpService = new HttpService()
assertEquals content.text, httpService.getContentFor("http://www.foo.com")
}
}
}
// This is the intended implementation.
class HttpService {
def getContentFor(url) {
new URL(url).content.text
}
}
// This intentionally wrong implementation also passes the test!
class HttpService {
def getContentFor(url) {
new URL("http://www.wrongurl.com").content.text
}
}
What does mocking the URL get you? It makes the test difficult to write. You won't be able to react to feedback the mock objects give you about the design of the API of the URL class, because it's not under your control. And if you don't precisely fake the behaviour of the URL and what it exposes about the HTTP protocol, the test will not be reliable.
You want to test that your "HttpService" object actually loads the data correctly from a given URL, copes with different content type encodings correctly, handles different classes of HTTP status code appropriately, and so forth. When I need to test this kind object -- one that merely wraps some underlying technical infrastructure -- I write a real integration test that verifies that the object really does use the underlying technology correctly.
For HTTP I write a test that creates an HTTP server, plugs a servlet into the server that will return some canned data, passed the URL of the servlet to the object to make it load the data, check that the loaded result is the same as the canned data used to initialise the servlet, and stop the server in the fixture tear-down. I use Jetty or the simple HTTP server that is bundled with JDK 6.
I'd only use mock objects to test the behaviour of objects that talk to the interface(s) of that object I've integration tested.
Putting on my "Programming in the Small" and "Unit test 100%" hat, you could consider this as a single method that does too many things. You could refactor the HttpService to:
class HttpService {
def newURLFrom(urlString) {
new URL(urlString)
}
def getContentText(url) {
url.content.text
}
def getContentFor(urlString) {
getContentText(newURLFrom(urlString))
}
}
This would give you a few more options for testing, as well as split out the factory aspect from the property manipulation. The testing options are bit more mundane then:
class HttpServiceTests extends GroovyTestCase {
def urlString = "http://stackoverflow.com"
def fauxHtml = "<html><body>Hello, world</body></html>";
def fauxURL = [content : [text : fauxHtml]]
void testMakesURLs() {
assertEquals(urlString,
new HTTPService().newURLFrom(urlString).toExternalForm())
}
void testCanDeriveContentText() {
assertEquals(fauxHtml, new HTTPService().getContentText(fauxURL));
}
// Going a bit overboard to test the line combining the two methods
void testGetsContentForURL() {
def service = new HTTPService()
def emc = new ExpandoMetaClass( service.class, false )
emc.newURLFrom = { input -> assertEquals(urlString, input); return fauxURL }
emc.initialize()
service.metaClass = emc
assertEquals(fauxHtml, service.getContentFor(urlString))
}
}
I think that this makes all the assertions that you want, but at the cost of sacrificing test readability in the last case.
I would agree with Nat about this making more sense as an integration test. (You are integrating with Java's URL library on some level.) But assuming that this example simplifies some complex logic, you could use the metaclass to override the instances class effictvely partially mocking the instance.
It's tough to mock out JDK classes that are declared final... Your problem, as you reference through the enhancement, is that there is no way to create a URL other than calling the constructor.
I try to separate the creation of these kinds of objects from the rest of my code; I'd create a factory to separate the creation the URLs. This should be simple enough to not warrant a test. Others take a typical wrapper/decorator approach. Or you may be able to apply the adapter pattern to translate to domain objects that you write.
Here is a similar answer to a surprisingly similar problem: Mocking a URL in Java
I think this demonstrates something that a lot of people learn after doing more testing: the code we write to make things more testable is meant to isolate what we desire to test from what we can safely say is already tested somewhere else. It's a fundamental assumption we have to make in order to do unit testing. It can also provide a decent example of why good unit tests aren't necessarily about 100% code coverage. They have to be economical, too.
Hope this helps.
What exactly are you expecting to have fail? It is not readily apparent what you are trying to test with that code. By Mocking URL.getContent you are telling Groovy to always return the variable content when URL.getContent() is invoked. Are you wishing to make the return value of URL.getContent() conditional based upon the URL string? If that is the case, the following accomplishes that:
import grails.test.*
import groovy.mock.interceptor.MockFor
class HttpServiceTests extends GrailsUnitTestCase {
def connectionUrl
void testGetsContentForURL() {
// put the desired "correct" URL as the key in the next line
def content = ["http://www.foo.com" : "<html><body>Hello, world</body></html>"]
def mockURLContext = new MockFor(URL.class)
mockURLContext.demand.getContent { [text : content[this.connectionUrl]] }
mockURLContext.use {
def httpService = new HttpService()
this.connectionUrl = "http://www.wrongurl.com"
assertEquals content.text, httpService.getContentFor(this.connectionUrl)
}
}
}