Im trying to test this suspend function:
suspend fun <T> getResult(call: suspend () -> Response<T>): Resource<T> {
val response = call()
val body = response.body()
val code = response.code()
if (response.isSuccessful) {
Log.d(Cybrid.instance.tag, "Data: ${response.code()} - ${response.body()}")
return Resource.success(body!!, code)
} else if (response.code() == HTTP_UNAUTHORIZED || response.code() == HTTP_FORBIDDEN) {
Cybrid.instance.let { cybrid ->
cybrid.listener.let {
cybrid.invalidToken = true
it?.onTokenExpired()
}
}
Log.e(Cybrid.instance.tag, "Error - Something wrong with TOKEN : ${response.code()} ${response.message()}")
return Resource.error(response.message(), code=response.code())
} else {
Log.e(Cybrid.instance.tag, "Error - Other: ${response.code()} ${response.message()} :: ${response.raw()}")
return Resource.error(message = response.message(), data= response.body(), code= response.code())
}
}
With this Unit Test Case, all it cover except line 13 , I dont know how to cover the line!!!
#ExperimentalCoroutinesApi
#Test
fun get400ErrorServerTest() = runBlocking {
Cybrid.instance.setBearer("Bearer")
val pricesService = AppModule.getClient().createService(PricesApi::class.java)
val result = getResult { pricesService.listPrices() }
Assert.assertNotNull(result)
Assert.assertEquals(result.code, 400)
Assert.assertNull(result.data)
}
The coverage report says:
Some idea to get coverage for line 13 ???
Thnanks
As I know, one way to verify test result is to write expressions into the then section, that evaluates to boolean.
However lately I experienced a behavior which I don't understand. It seems like when one tries to verify something, that is in a block, then assertion only works with an explicit assert keyword.
Here is the example. I wrote a dummy if statement to have a block, but it's the same with a for loop or any control flow.
def "test fails as expected"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
result == "otherValue"
}
def "test passes, but shouldn't"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
if (true) {
result == "otherValue"
}
}
def "test fails as expected when using assert"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
if (true) {
assert result == "otherValue"
}
}
I find this behavior a bit misleading. Can someone explain why it works that way? Is this a bug or the usage is incorrect?
Following Spock documentation:
The when and then blocks always occur together. They describe a stimulus and the expected response. Whereas when blocks may contain arbitrary code, then blocks are restricted to conditions, exception conditions, interactions, and variable definitions. A feature method may contain multiple pairs of when-then blocks.
This explains why Spocks AST transformer does not see the following then block:
then:
if (true) {
result == "otherValue"
}
as a correct one and it does not transform it to SpockRuntime.verifyCondition() invocation.
If you compile your class (with static compilation enabled for better readability) and check the bytecode you will see something similar to this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.spockframework.runtime.ErrorCollector;
import org.spockframework.runtime.SpockRuntime;
import org.spockframework.runtime.ValueRecorder;
import org.spockframework.runtime.model.BlockKind;
import org.spockframework.runtime.model.BlockMetadata;
import org.spockframework.runtime.model.FeatureMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Specification;
#SpecMetadata(
filename = "OtherSpec.groovy",
line = 4
)
public class OtherSpec extends Specification implements GroovyObject {
public OtherSpec() {
}
public Object test(String result) {
return true ? ScriptBytecodeAdapter.compareEqual(result, "otherValue") : null;
}
#FeatureMetadata(
line = 7,
name = "test fails as expected",
ordinal = 0,
blocks = {#BlockMetadata(
kind = BlockKind.WHEN,
texts = {"result has some value"}
), #BlockMetadata(
kind = BlockKind.THEN,
texts = {"result has the expected value"}
)},
parameterNames = {}
)
public void $spock_feature_0_0() {
ErrorCollector $spock_errorCollector = new ErrorCollector(false);
ValueRecorder $spock_valueRecorder = new ValueRecorder();
Object var10000;
try {
String result = "someValue";
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(12), Integer.valueOf(9), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "result == \"otherValue\"", Integer.valueOf(12), Integer.valueOf(9), (Object)null, var13);
var10000 = null;
} finally {
;
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
#FeatureMetadata(
line = 15,
name = "test passes, but shouldn't",
ordinal = 1,
blocks = {#BlockMetadata(
kind = BlockKind.WHEN,
texts = {"result has some value"}
), #BlockMetadata(
kind = BlockKind.THEN,
texts = {"result has the expected value"}
)},
parameterNames = {}
)
public void $spock_feature_0_1() {
String result = "someValue";
if (true) {
ScriptBytecodeAdapter.compareEqual(result, "otherValue");
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
}
#FeatureMetadata(
line = 25,
name = "test fails as expected when using assert",
ordinal = 2,
blocks = {#BlockMetadata(
kind = BlockKind.WHEN,
texts = {"result has some value"}
), #BlockMetadata(
kind = BlockKind.THEN,
texts = {"result has the expected value"}
)},
parameterNames = {}
)
public void $spock_feature_0_2() {
ErrorCollector $spock_errorCollector = new ErrorCollector(false);
ValueRecorder $spock_valueRecorder = new ValueRecorder();
Object var10000;
try {
String result = "someValue";
DefaultGroovyMethods.println(this, this.test("otherValue"));
var10000 = null;
if (true) {
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, var13);
var10000 = null;
} finally {
;
}
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
}
Now, if we analyze this code, we will find out that the following Spock test case:
def "test fails as expected"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
result == "otherValue"
}
compiles to something like this:
public void $spock_feature_0_0() {
ErrorCollector $spock_errorCollector = new ErrorCollector(false);
ValueRecorder $spock_valueRecorder = new ValueRecorder();
Object var10000;
try {
String result = "someValue";
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(12), Integer.valueOf(9), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "result == \"otherValue\"", Integer.valueOf(12), Integer.valueOf(9), (Object)null, var13);
var10000 = null;
} finally {
;
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
And the test case where you put assertion inside if-statement:
def "test passes, but shouldn't"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
if (true) {
result == "otherValue"
}
}
compiles to something like this:
public void $spock_feature_0_1() {
String result = "someValue";
if (true) {
ScriptBytecodeAdapter.compareEqual(result, "otherValue");
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
}
If you are interested in investigating the source code of this AST transformation you can start by analyzing:
org.spockframework.compiler.SpecRewriter.visitThenBlock() method
org.spockframework.compiler.DeepBlockRewriter.handleImplicitCondition() method
And for the last use case - adding assert to the if-statement block is the explicit instruction for Spock that it has to be transformed to verification condition invocation. That's why you see the bytecode that decompiles to something like this:
public void $spock_feature_0_2() {
ErrorCollector $spock_errorCollector = new ErrorCollector(false);
ValueRecorder $spock_valueRecorder = new ValueRecorder();
Object var10000;
try {
String result = "someValue";
DefaultGroovyMethods.println(this, this.test("otherValue"));
var10000 = null;
if (true) {
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, var13);
var10000 = null;
} finally {
;
}
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
Notice that if (true) { /*...*/ } is still present because AST transformer still ignores transforming it, but the condition:
assert result == "otherValue"
was visited and accepted by the AST transformer, and replaced by:
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
I am updating unit tests in a Grails project. We were originally using version 1.3.9 and now we are updating to version 2.3.9. I am using Spock.
I keep getting this error:
results:
junit.framework.AssertionFailedError: Condition not satisfied:
controller.edit() == [filterCategoryInstance: filterCategoryInstance]
| | | |
| null false John
com.xxxxxx.xxxxx.FilterCategoryController#20574000
Here is the controller code:
#Secured(["hasAnyRole('CM_ADMIN')"])
def edit() {
def filterCategoryInstance = FilterCategory.get(params.id)
if (!filterCategoryInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'dpFilterCategory.label', default: 'FilterCategory'), params.id])}"
redirect(action: "list")
}
else {
return [filterCategoryInstance: filterCategoryInstance]
}
}
and here is the test code:
#Mock([FilterCategory, FilterCategoryTag])
#TestFor(FilterCategoryController)
#TestMixin(DomainClassUnitTestMixin)
class FilterCategoryControllerSpec extends ExtendedControllerSpec {
def 'edit action: existing FilterCategory'() {
setup:
mockI18N(FilterCategoryController)
params.id = filterCategoryInstance.id
expect:
controller.edit() == [filterCategoryInstance: filterCategoryInstance]
where:
tag = new FilterCategoryTag(name: 'tag1')
filterCategoryInstance = new FilterCategory(name: "John",
submissionText:"John", sortOrder:0, 'filterCategoryTags': [tag])
}
And here is the ExtendedControllerSpec code. I hope I have included enough code:
I have looked at the following web pages for guidance:
#Mixin(MetaClassMixin)
class ExtendedControllerSpec extends Specification {
def props
protected void setup() {
//super.setup()
props = new Properties()
File file = new File("grails-app/i18n/messages.properties")
if (file.exists()) {
def stream = new FileInputStream(file)
props.load stream
stream.close()
}
mockI18N(controller)
}
def mockI18N = { controller ->
controller.metaClass.message = { Map map ->
if (!map.code)
return ""
if (map.args) {
def formatter = new MessageFormat("")
if (props.getProperty(map.code)) {
formatter.applyPattern props.getProperty(map.code)
}
return formatter.format(map.args.toArray())
} else {
if (props && props.hasProperty(map.code)) {
return props.getProperty(map.code)
} else {
return map.code
}
}
}
}
/**
* add dynamic methods in test setup.
*/
protected void addDynamicMethods() {
registerMetaClass(String)
String.metaClass.mixin StringUtils
}
protected GrailsUser mockGrailsUser() {
return Mock(GrailsUser)
}
...
/**
* must call AFTER mockDpSercurityService
*/
protected void setHasRoleTrue() {
if (controller?.dpSecurityService?.metaClass) {
controller.dpSecurityService.metaClass.hasRole = {return true}
}
}
protected void setHasRoleFalse() {
if (controller?.dpSecurityService?.metaClass) {
controller.dpSecurityService.metaClass.hasRole = {return false}
}
}
protected void mockUserService() {
controller.dpUserService = new MockFor(UserService)
}
}
http://sanjaykanwar.blogspot.com/2012/07/grails-controller-test-with-spock.html
http://naleid.com/blog/2012/05/01/upgrading-to-grails-2-unit-testing
Looks like the if branch gets executed in edit() instead of the else branch because FilterCategory does not get saved and therfore does not get a proper id.
someone, please, explain why domain class behaves different when is mocked with new MockFor(..)?
I use grails 2.4.1.
I have create domain class GTest:
class GTest {
static constraints = {
}
String amethod() {
return "GTest.amethod"
}
}
And a groovy class DTest:
class DTest {
String amethod() {
return "DTest.amethod"
}
}
And here a groovy test:
class GroovyJUnitTest {
#Test
void testDTestAmethod() {
def mock = new MockFor(DTest)
mock.demand.amethod {
return "Mock DTest"
}
mock.use {
def dtest = new DTest()
// Here dtest.metaClass=groovy.mock.interceptor.MockProxyMetaClass#...
def ret = dtest.amethod()
// This assertation successes
assertEquals "Mock DTest", ret
}
}
#Test
void testGTestAmethod() {
def mock = new MockFor(GTest)
mock.demand.amethod {
return "Mock GTest"
}
mock.use {
def gtest = new GTest()
// Here dtest.metaClass=groovy.lang.ExpandoMetaClass#...
def ret = gtest.amethod()
// This assertation fails
assertEquals "Mock GTest", ret
}
}
}
So, this question is how should Domain class be mocked programmatically?
Thank you for explanation.
You should annotation your test with grails.test.mixin.TestFor.
#grails.test.mixin.TestFor(MyDomainClass)
class MyDomainClassTest {
// ...
}
See http://grails.org/doc/latest/guide/testing.html#unitTestingDomains for more details.
I am currently practicing test driven development in groovy using spock.
I have 1 set of tests where 3 different implementations doing the same thing: iterative, recursive, and memoized.
so I have created an abstract class to hold the tests, and then created 3 different files to return the concrete class implementation to run the tests. I have iterative and recursive working, but I am having issues with memoize()
import spock.lang.Specification
abstract class FibonacciTest extends Specification {
private calculator
abstract def getCalculator()
def setup() {
calculator = getCalculator()
}
def "test canary"() {
expect:
true
}
// more tests
}
class RecursiveFibonacciTest extends FibonacciTest {
def getCalculator() {
new RecursiveCalculator()
}
}
class IterativeFibonacciTest extends FibonacciTest {
def getCalculator() {
new IterativeCalculator()
}
}
class MemoizeFibonacciTest extends FibonacciTest {
def getCalculator() {
new MemoizeCalculator()
}
}
class RecursiveCalculator {
def getFibonacci(position) {
if (position < 2) {
1
}
else {
getFibonacci(position - 1) + getFibonacci(position - 2)
}
}
}
class IterativeCalculator {
def getFibonacci(position) {
if (position < 2) {
1
}
else {
def value = 1
def previousValue = 1
for (i in 2..position) {
def temporaryValue = previousValue
previousValue = value
value = temporaryValue + previousValue
}
value
}
}
}
So I've got the iterative and recursive working, but having problems getting the memoize to work.. I think it should work with this, but its not.. anyone know what I'm doing wrong?
class MemoizeCalculator {
def getFibonacci = { position ->
if (position < 2)
1
else
getFibonacci.call(position - 1) + getFibonacci.call(position - 2)
}.memoize()
}
You can't reference the getFibonacci variable from the same statement that declares it. Either change getFibonacci.call to call, or declare the variable (def getFibonacci) before assigning it (getFibonacci = ...).