Is it possible to directly set a System.property for a particular method of a Grails unit test class?
I've got a Filters class that blocks some actions if a system property is set in Config.groovy:
class MyFilters {
def filters =
{
if ('true'.equals(System.properties.getProperty('myProperty', 'false'))){
writeFilter(controller: "myController", action: "update")
{
before =
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
In MyControllerTests, I'm trying to implement a test for the filter:
void testMyFilter(){
System.properties.setProperty('myProperty', 'true')
withFilters(action:"update") {
controller.update()
}
assert response.status == HttpServletResponse.SC_FORBIDDEN
}
But the filter class is already mocked at this stage (using the default 'false' property value from Config) so the change of the property in the test has no effect.
Is there a way around this, or a way to force Grails to reload the Filters class after the prop is changed? I can't change the default property in Config as the other controller tests will break, it's just for a particular environment where this prop will be set so I'd like the scenario covered by a test that checks the Filter will kick in.
Thanks.
Inevitably, I found out how to do it about a minute after I posted this. Adding mockFilters(MyFilters) after the setProperty makes Grails rebuild the mocked filter with the updated property.
Related
Let's say I have the following method in a service:
private void deleteItems(List<Item> itemsToDelete) {
def sql = new Sql(dataSource)
itemsToDelete?.each { Item item ->
sql.execute("DELETE FROM owner_item WHERE item_id = ${item.id}")
item.delete(flush: true, failOnError: true)
flushDatabaseSession();
}
}
How do I create a test for this method in the ItemServiceSpec? When I try it, I get either a DataSource "Must specify a non-null Connection" error or a nullPointerException on sql.
This is my existing test.
#TestFor(ItemService)
#Mock([Item])
#Build([Item])
class SubjectServiceSpec extends Specification {
...
def "delete items"() {
given:
Item item1 = Item.build().save(flush: true)
Item item2 = Item.build().save(flush: true)
Item.count() == 2
DataSource mockDataSource = Mock()
service.dataSource = mockDataSource
1 * deleteItems
when:
service.deleteItems([item1, item2])
then:
Item.count() == 0
}
}
What you are trying to do here, is to mock a dependency (DataSource) of a dependency (Sql). This normally leads to a situation, where you a not 100% aware of how the Sql interacts with the DataSource Object. If Sql changes private interaction with the Datasource in a Version Update, you have to deal with the situation.
Instead of mocking a dependency of a dependency you should the Sql Class directly. For this, the sql has to be some kind of explicit dependency that you can receive via DI or a method parameter. In this case you can just mock the execute call like so (choosen the way of a Expando-Mock, but you could also use Map or the Mock Stuff from Spock):
given:
def sqlMock = new Expando()
sqlMock.execute = { return 'what ever you want or nothing, because you mock a delete operation' }
service.sql = sqlMock
when:
service.deleteItems([item1, item2])
then:
assertItemsAreDeletedAndTheOwnerAsWell()
Thinking about the whole testcase, there a two major problems in my opinion.
The first one is, when you ask yourself what kind of certainty do you really get here by mocking out the whole sql stuff. In this case, the only thing that you are doing here is to interact with the db. When you mock this thing out, then there is nothing anymore that you could test. There is not many conditional stuff or anything that should be backed up by a unit test. Due to this, I would suggest to write only integration spec for this test-case where you have something like a H2DB for testing purposes inplace.
The second thing is, that you actually don't need the Sql Manipulation at all. You can configure GORM and Hibernate in a way do a automatic and transparent deletion of the owner of the item, if the item is deleted. For this, look at the docs (especially the cascade part) from GORM or directly in the Hibernate docs.
To sum it up: use cascade: 'delete' together with a proper integration test and you have a high amount of certainty and less boilerplate code.
I want to run a unit test for a service. The method I want to test includes a some log.debug() statements. While the log property is injected at runtime, it does not seem to be injected in tests, so it throws groovy.lang.MissingPropertyException: No such property: log for class:
This is my unit test class:
#TestFor(ServiceUnderTest)
#Mock([ServiceUnderTest])
class ServiceUnderTestTests {
def test() {
def mock = [ mockedProp: [...] ] as ServiceUnderTest
def info = mock.doOperation()
assert ....
}
}
I've also tried adding MockUtils.mockLogging(ServiceUnderTest) but with no success.
How can I get the log property properly injected in my service class while in unit tests?
You do not have to have the test class annotated with #Mock([ServiceUnderTest]). #TestFor(ServiceUnderTest) detects its a service class and does all the mocking automatically. It also adds a service property to the test class that can be accessed in all the test methods and mocks the log property accordingly.
I think the problem why neither mocking nor explicit log mocking with MockUtils.mockLogging(ServiceUnderTest) does work in your case is the as coercion you are using in your test method code:
def mock = [ mockedProp: [...] ] as ServiceUnderTest
Groovy internally uses java.lang.reflect.Proxy to create a proxy descendant class from ServiceUnderTest. The proxy class does not see changes done to the ServiceUnderTest meta class like the added log property.
I would solve this issue by using a per-object meta class. You can mock the property getter (or setter) by altering the metaClass of the service object. Be aware that meta-class changes are rolled back by Grails in-between execution of test methods:
service.metaClass.mockedProp = {-> ... }
I have used manually written as well as Grails generated Unit tests for this command object:
package myapp
#grails.validation.Validateable
class SearchCommand {
String basisBuild
String buildToSearch
static constraints = {
basisBuild(blank: false)
}
}
After having my hand written unit test fail I used Grails:
create-unit-test myapp.SearchCommand
I filled in the Unit Test, and made an assertion that should pass per documentation on mocked constraints:
package myapp
import static org.junit.Assert.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import org.junit.*
#TestMixin(GrailsUnitTestMixin)
class SearchCommandTests {
void setUp() {
mockForConstraintsTests(SearchCommand)
}
void tearDown() {
// Tear down logic here
}
void testSomething() {
SearchCommand commandUnderTest = new SearchCommand()
commandUnderTest.validate(basisBuild: "")
assertEquals "blank", commandUnderTest.errors['basisBuild']
}
}
Why am I getting this failure?
grails> test-app
| Running 9 unit tests... 9 of 9
| Failure: testSomething(com.siemens.soarian.sf.gap.SearchCommandTests)
| java.lang.AssertionError: expected:<blank> but was:<null>
at org.junit.Assert.fail(Assert.java:93)
I believe I found the grails supported way to unit test Command objects in grails 2.0. You need to use mockCommandObject provided by the ControllerUnitTestMixin.
Credit to Erik
http://www.jworks.nl/2012/04/12/testing-command-objects-in-grails-2-0/
EDIT
Using validate() appropriately and mockForConstraintsTest should work if the patch mentioned in the existing Grails bug is in place (Thanks to #codelark for bringing that up). In order to test the command object from a Web App standpoint (using controller) the below information would be helpful.
Test Command Object Using Controller action:-
A command object is only deemed as such when it is used as a parameter in one of the action method inside a controller. Refer Command Objects (Warning NOTE).
Use SearchCommand in an action method, you should be able to assertEquals.
Sample:
void testSomething() {
YourController controller = mockController(YourController) //Or instantiate
SearchCommand commandUnderTest = new SearchCommand ()
//Note the usage here. validate() does not take parameters
commandUnderTest.basisBuild = ''
commandUnderTest.validate()
//Call your action
controller.searchCommandAction(commandUnderTest)
assert response.text == 'Returned'
assertEquals "blank", commandUnderTest.errors['basisBuild']
}
YourController's action:-
def searchCommandAction(SearchCommand sc){
render "Returned"
}
Note:
With out the patch from the grails bug we see the below error in #Grails 2.1.4, 2.2.0 & 2.2.1
I get an error when I only correct the validation and use mockForConstraintTests without using controller action:
You are using the validate method incorrectly. You never set the field on the class, so the field is null, not blank. Try changing your test as follows:
void testSomething() {
SearchCommand commandUnderTest = new SearchCommand()
commandUnderTest.basisBuild = ""
assertFalse commandUnderTest.validate()
assertEquals 'blank', commandUnderTest.errors['basisBuild']
}
Edit: There is also a grails bug when testing command classes that use the #Validatable annotation. There are some workarounds in the bug commentary.
I have a controller action that checks a condition and based on the condition's result, it will set a field to session variable
Controller:
def checkUser={
if(condition: true){
session.user = id
}else{
session.user=null
}
}
Now, in my testcase, if I have to assert on this session's user value, how do i do it?
I tried it like this, but it did not work:
void testSomething(){
controller.checkUser()
assertNull(session.user)
}
Any idea on how to assert session variable which is being set in the controller?
Make sure your test class extends ControllerUnitTestCase or is using the test Mixin.
CUTC offers several mocks to facilitate Controller testing. Here's what you'll do:
void testSomething(){
controller.checkUser()
assertNull(mockSession.user)
}
Here's the doc: http://grails.org/doc/1.3.x/api/grails/test/ControllerUnitTestCase.html
If you're using grails 2.0+: http://grails.org/doc/latest/api/grails/test/MvcUnitTestCase.html
BaseControllerTest.PrepareController is enough for controller properties setup, such as PropertyBag and Context
[TestClass]
public ProjectsControllerTest : BaseControllerTest
{
[TestMethod]
public void List()
{
// Setup
var controller = new ProjectsController();
PrepareController(controller);
controller.List();
// Asserts ...
Assert.IsInstanceOfType(typeof(IEnumerable<Project>),controller.PropertyBag["Projects"]);
}
}
But now to run the entire pipeline for integration testing, including filters declared in action attributes?
EDIT:
I'm not interested in view rendering, just the controller logic along with declarative filters.
I like the idea of moving significant amount of view setup logic into action filters, and i'm not sure if i need extra level of integration tests, or is it better done with Selenium?
you can get a hold of the filters, and run them.
so, assuming action is Action<YourController>, and controller is an instance of the controller under test,
var filtersAttributes = GetFiltersFor(controller); // say by reflecting over its attributes
var filters = filtersAttributes
.OrderBy(attr => attr.ExecutionOrder)
.Select(attr => new { Attribute = attr, Instance =
(IFilter)Container.Resolve(attr.FilterType) }); // assuming you use IoC, otherwise simply new the filter type with Activator.CreateInstance or something
Action<ExecuteWhen> runFilters = when =>
{
// TODO: support IFilterAttributeAware filters
foreach (var filter in filters)
if ((filter.Attribute.When & when) != 0)
filter.Instance.Perform(when, Context, controller, controllerContext);
};
// Perform the controller action, including the before- and after-filters
runFilters(ExecuteWhen.BeforeAction);
action(controller);
runFilters(ExecuteWhen.AfterAction);
Getting the view-engine to play is trickier (though possible), but I think that testing generated views along with the controller logic is involving way too many moving and incur unjustified maintenance effort