Documentation is available at https://testing.grails.org/
Grails 3.x branch is 1.1.x.
Grails 4.0.x branch is 2.1.x.
Grails 5 branch is 2.4.x which includes support for Groovy 3.
Please checkout release notes for more information.
Trait-based testing library for Grails framework
Home Page: http://testing.grails.org
License: Apache License 2.0
Documentation is available at https://testing.grails.org/
Grails 3.x branch is 1.1.x.
Grails 4.0.x branch is 2.1.x.
Grails 5 branch is 2.4.x which includes support for Groovy 3.
Please checkout release notes for more information.
$ ./gradlew groovydoc
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.0/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
> Task :groovydoc FAILED
Exception in thread "main" java.lang.NoClassDefFoundError: picocli/CommandLine$ParameterException
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getDeclaredConstructors(Class.java:2020)
at org.codehaus.groovy.reflection.CachedClass$2$1.run(CachedClass.java:88)
at java.security.AccessController.doPrivileged(Native Method)
at org.codehaus.groovy.reflection.CachedClass$2.initValue(CachedClass.java:86)
at org.codehaus.groovy.reflection.CachedClass$2.initValue(CachedClass.java:81)
at org.codehaus.groovy.util.LazyReference.getLocked(LazyReference.java:50)
at org.codehaus.groovy.util.LazyReference.get(LazyReference.java:37)
at org.codehaus.groovy.reflection.CachedClass.getConstructors(CachedClass.java:310)
at groovy.lang.MetaClassImpl.<init>(MetaClassImpl.java:218)
at groovy.lang.MetaClassImpl.<init>(MetaClassImpl.java:228)
at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createNormalMetaClass(MetaClassRegistry.java:171)
at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:161)
at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:144)
at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:288)
at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:331)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:271)
at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:969)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:86)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:59)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:237)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:249)
at org.codehaus.groovy.tools.groovydoc.Main.main(Main.groovy:62)
Caused by: java.lang.ClassNotFoundException: picocli.CommandLine$ParameterException
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 24 more
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':groovydoc'.
> Process 'command '/Users/sdelamo/.sdkman/candidates/java/8.0.191-oracle/bin/java'' finished with non-zero exit value 1
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.0/userguide/command_line_interface.html#sec:command_line_warnings
(moving issue from grails/grails-data-mapping#1020 to here)
See https://github.com/jeffbrown/servicetestproblem
Domain:
package servicetestproblem
class Person {
String name
}
Data Service:
package servicetestproblem
import grails.gorm.services.Service
@Service(Person)
abstract class PersonService {
abstract List<Person> list()
}
Unit test:
package servicetestproblem
import grails.testing.gorm.DataTest
import grails.testing.services.ServiceUnitTest
import spock.lang.Specification
class PersonServiceSpec extends Specification implements ServiceUnitTest<PersonService>, DataTest {
def setupSpec() {
mockDomain Person
}
void 'first test'() {
expect:
service.list().size() == 0
}
void 'second test'() {
expect:
service.list().size() == 0
}
}
There is no reason to have a unit test like that of course but this is a simple example that demonstrates the problem. The second test method will fail with the following:
Condition failed with Exception:
service.list().size() == 0
|
java.lang.reflect.InvocationTargetException
at servicetestproblem.PersonServiceSpec.second test(PersonServiceSpec.groovy:20)
Caused by: java.lang.reflect.InvocationTargetException
at grails.testing.services.ServiceUnitTest$Trait$Helper.mockArtefact(ServiceUnitTest.groovy:66)
at org.grails.testing.ParameterizedGrailsUnitTest$Trait$Helper.getArtefactInstance(ParameterizedGrailsUnitTest.groovy:48)
at grails.testing.services.ServiceUnitTest$Trait$Helper.getService(ServiceUnitTest.groovy:83)
... 1 more
Caused by: java.lang.IllegalStateException: Could not register object [servicetestproblem.$PersonServiceImplementation@42339ea2] under bean name 'personService': there is already object [servicetestproblem.$PersonServiceImplementation@42339ea2] bound
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.registerSingleton(DefaultSingletonBeanRegistry.java:130)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerSingleton(DefaultListableBeanFactory.java:936)
at grails.testing.gorm.DataTest$Trait$Helper.mockDataService(DataTest.groovy:101)
... 4 more
Implementing complete GraphQL support involves reading the request body and considering it as the query for one special case. In a controller, this means using request.reader.text
.
def index() {
def body = request.reader.text
respond body
}
In a test class the controller method is called multiple times as there are multiple tests.
def "test"() {
when:
controller.index()
then:
true
}
def "test2"() {
when:
controller.index()
then:
true
}
This is a problem when the body is empty. When executing the second test, it fails with java.io.IOException: Stream closed
. The tests are not independent.
Please note that when the body is not empty, i.e.
request.json = [hello: "world"]
the tests pass.
Please also note that replacing request.reader.text
with request.JSON
in the controller causes the tests to pass. This is specifically for reading the body text.
See: a1c66a2
If the embedded container is configured to use HTTPS, the geb.build.baseUrl
property is set to http
, not https
. This is hard coded in the following file:
I have probably misunderstood the Manipulating Configuration docs.
Given this service:
import grails.config.Config
import grails.core.support.GrailsConfigurationAware
import groovy.transform.CompileStatic
@CompileStatic
class SignupService implements GrailsConfigurationAware {
Boolean allowSignup
@Override
void setConfiguration(Config co) {
allowSignup = co.getProperty('mc.allow.signup', Boolean)
}
boolean isSignupAllowed() {
allowSignup
}
}
and this test:
import grails.testing.services.ServiceUnitTest
import spock.lang.Specification
class SignupServiceSpec extends Specification implements ServiceUnitTest<SignupService> {
Closure doWithConfig() {{ config ->
config.mc.allow.signup = true
}}
def "singup is allowed if configuration parameter is set"() {
expect:
service.isSignupAllowed()
}
}
I expected the previous test to pass but it fails.
I am trying to build a plugin with multitenency feature that carries all the domain object so that it can be shared across multiple grails based web-application, but on writing a simple unit-test I am seeing that the Unit-test denies to identify the Domain class in the plugin as a domain at all.
If I run the same test by linking it with the Domain object in the project itself instead of Plugin, everything works fine, I cannot have the test placed in plugin itself as the heavy implementation will be on the specific service end, any idea on it, below is the code snippet, where Benefit is the domain obect that lies inside the inplace plugin:-
package test
import com.common.domain.Benefit
import grails.test.hibernate.HibernateSpec
import grails.testing.services.ServiceUnitTest
import org.grails.datastore.mapping.config.Settings
import org.grails.datastore.mapping.multitenancy.resolvers.SystemPropertyTenantResolver
class TestServiceSpec extends HibernateSpec implements ServiceUnitTest<TestService>{
Class<?>[] getDomainClassesToMock(){
return [Benefit] as Class[]
}
@Override
Map getConfiguration() {
[(Settings.SETTING_MULTI_TENANT_RESOLVER_CLASS): SystemPropertyTenantResolver]
}
def setup() {
System.setProperty(SystemPropertyTenantResolver.PROPERTY_NAME, 'vendor1') //
}
def cleanup() {
System.setProperty(SystemPropertyTenantResolver.PROPERTY_NAME, '')
}
void "test something"() {
expect:"fix me"
service.serviceMethod()
true == true
}
}
here is the service
.
.
import com.common.domain.Benefit
@Transactional
@CurrentTenant
@CompileStatic
class TestService {
def serviceMethod() {
def something = Benefit.findAll()
println something
return something
}
}
Following is the error I am gettin on the end of the unit test
Either class [com.common.domain.Benefit] is not a domain class or GORM has not been initialized correctly or has already been shutdown. Ensure GORM is loaded and configured correctly before calling any methods on a GORM entity.
java.lang.IllegalStateException: Either class [com.common.domain.Benefit] is not a domain class or GORM has not been initialized correctly or has already been shutdown. Ensure GORM is loaded and configured correctly before calling any methods on a GORM entity.
at org.grails.datastore.gorm.GormEnhancer.stateException(GormEnhancer.groovy:472)
at org.grails.datastore.gorm.GormEnhancer.findDatastore(GormEnhancer.groovy:354)
at org.grails.datastore.gorm.GormEnhancer.findTenantId(GormEnhancer.groovy:268)
at org.grails.datastore.gorm.GormEnhancer.findStaticApi(GormEnhancer.groovy:299)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.currentGormStaticApi(GormEntity.groovy:1368)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.findAll(GormEntity.groovy:689)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.findAll(GormEntity.groovy:688)
at test.TestService.$tt__serviceMethod(TestService.groovy:15)
at test.TestService._mt__serviceMethod_closure3(TestService.groovy)
at groovy.lang.Closure.call(Closure.java:418)
at groovy.lang.Closure.call(Closure.java:434)
at grails.gorm.transactions.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:94)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at grails.gorm.transactions.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:91)
at test.TestService.serviceMethod_closure1(TestService.groovy)
at groovy.lang.Closure.call(Closure.java:418)
at groovy.lang.Closure.call(Closure.java:434)
at grails.gorm.multitenancy.Tenants.withId_closure2$_closure6(Tenants.groovy:265)
at org.grails.orm.hibernate.GrailsHibernateTemplate$1.doInHibernate(GrailsHibernateTemplate.java:153)
at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:299)
at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:243)
at org.grails.orm.hibernate.GrailsHibernateTemplate.executeWithNewSession(GrailsHibernateTemplate.java:150)
at org.grails.orm.hibernate.GrailsHibernateTemplate.executeWithExistingOrCreateNewSession(GrailsHibernateTemplate.java:209)
at org.grails.orm.hibernate.AbstractHibernateDatastore.withNewSession(AbstractHibernateDatastore.java:369)
at grails.gorm.multitenancy.Tenants.withId_closure2(Tenants.groovy:258)
at grails.gorm.multitenancy.Tenants$CurrentTenant.withTenant(Tenants.groovy:358)
at grails.gorm.multitenancy.Tenants.withId(Tenants.groovy:236)
at org.grails.datastore.gorm.services.DefaultTenantService.withCurrent(DefaultTenantService.groovy:71)
at test.TestServiceSpec.test something somethng(TestServiceSpec.groovy:38)
The project at https://github.com/jeffbrown/webrequestissue contains the following:
package webrequestissue
class DemoController {
static final SOME_CONSTANT = 42
def index(int x, int y) {
int sum = x + y
render "$x plus $y is $sum"
}
}
package webrequestissue
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
import spock.lang.Unroll
class DemoControllerSpec extends Specification implements ControllerUnitTest<DemoController> {
@Unroll('#x plus #y should be #sum')
void "test index"() {
when:
params.x = x
params.y = y
controller.index()
then:
response.text == "$x plus $y is $sum"
where:
x | y | sum
4 | 5 | 9
40 | 2 | controller.SOME_CONSTANT
}
}
Referring to controller
in the where:
block causes a NullPointerException
to be thrown at
webRequest
has not yet been initialized. I think it is normally initialized by WebSetupInterceptor
at
.With an interceptor like this:
class ActionNameInterceptor {
boolean before() {
println "$controllerName $actionName $params"
if (actionName) {
request.setAttribute('actionNameSet', 'action name set')
}
return true
}
}
When running, the actionName
is set, but when running tests, the actionName
is not set.
See https://github.com/sbglasius/grailsInterceptorBugs interceptorbug.ActionNameInterceptorSpec#Test interceptor actionName should be set to default index
and interceptorbug.ActionNameInterceptorSpec#Test interceptor actionName should be set to specific name
for tests
The outstanding issue is that when i create an app with create-app, and then do the examples in the doc, all the tests pass but i am getting a exception. Looks like a spring boot lifecyle exception. Note it does not occur in the demo code.
java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.web.context.support.GenericWebApplicationContext@6e9319f: startup date [Mon May 01 11:52:04 CDT 2017]; root of context hierarchy at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:404) at org.springframework.context.support.ApplicationListenerDetector.postProcessBeforeDestruction(ApplicationListenerDetector.java:97) at org.s
trying to write a ServiceUnitTest
and mock a data service that the service under test is using, i get a NoSuchMethodError
:
java.lang.NoSuchMethodError: org.grails.datastore.mapping.core.AbstractDatastore.getService(Ljava/lang/Class;)Lorg/grails/datastore/mapping/services/Service;
at grails.testing.gorm.DataTest$Trait$Helper.mockDataService(DataTest.groovy:96)
at myapp.DummyServiceSpec.setupSpec(DummyServiceSpec.groovy:11)
but looking at the code (i.e. the AbstractDatastore.getService
method signature), i cannot figure out whats wrong.
sample app will be referenced in a minute.
For the next domain name:
package demo
class Book {
String name
static constraints = {
}
}
and service:
package demo
import grails.gorm.services.Service
import groovy.transform.CompileStatic
@CompileStatic
@Service(Book)
abstract class BookService {
abstract List<Book> findAll()
}
The next test:
package demo
import grails.testing.gorm.DataTest
import grails.testing.services.ServiceUnitTest
import spock.lang.Specification
class BookServiceSpec extends Specification implements ServiceUnitTest<BookService>, DataTest {
def setup() {
mockDomain(Book)
}
void "test there are not book"() {
expect:
service.findAll().size() == 0
}
}
throws the error Cannot add Service class [class demo.BookService]. It is not a Service!
org.spockframework.runtime.ConditionFailedWithExceptionError at BookServiceSpec.groovy:19
Caused by: org.grails.core.exceptions.GrailsConfigurationException at BookServiceSpec.groovy:19
Condition failed with Exception:
service.findAll().size() == 0
|
org.grails.core.exceptions.GrailsConfigurationException: Cannot add Service class [class demo.BookService]. It is not a Service!
Condition failed with Exception:
service.findAll().size() == 0
|
org.grails.core.exceptions.GrailsConfigurationException: Cannot add Service class [class demo.BookService]. It is not a Service!
at demo.BookServiceSpec.test there are not book(BookServiceSpec.groovy:19)
Caused by: org.grails.core.exceptions.GrailsConfigurationException: Cannot add Service class [class demo.BookService]. It is not a Service!
at grails.core.DefaultGrailsApplication.addArtefact(DefaultGrailsApplication.java:855)
at grails.core.DefaultGrailsApplication.addArtefact(DefaultGrailsApplication.java:517)
at grails.testing.services.ServiceUnitTest$Trait$Helper.mockArtefact(ServiceUnitTest.groovy:37)
at org.grails.testing.ParameterizedGrailsUnitTest$Trait$Helper.getArtefactInstance(ParameterizedGrailsUnitTest.groovy:48)
at grails.testing.services.ServiceUnitTest$Trait$Helper.getService(ServiceUnitTest.groovy:51)
... 1 more
I have a custom TagLib that makes use of the asset-pipeline-grails (version 3.0.6) AssetsTagLib to display images. It calls asset.image() to output an image tag when it renders.
In order to test this, I have a CustomTagLibSpec that implements TagLibUnitTest, and in the setup method I was expecting to be able to do the following to bootstrap and mock the the relevant asset dependencies:
setup() {`
defineBeans(new AssetPipelineGrailsPlugin())
mockTagLib(AssetsTagLib)
mockTagLib(AssetMethodTagLib)
}
On running the unit test though, I get a failure because grailsApplication is used in the AssetPipelineGrailsPlugin.doWithSpring Closure, and that is not set.
If I change my unit test code to
setup() {`
def assetPlugin = new AssetPipelineGrailsPlugin()
assetPlugin.applicationContext = applicationContext
assetPlugin.grailsApplicatin = grailsApplication
defineBeans(assetPlugin)
mockTagLib(AssetsTagLib)
mockTagLib(AssetMethodTagLib)
}
Then my unit test works.
Given that all plugins implement Plugin, and therefore ApplicationContextAware, and GrailsApplicationAware, can the defineBeans(Object plugin) method in GrailsUnitTest be changed to set the grailsApplication and applicationContext from the unit test to avoid having to do it manually please ?
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
org.spockframework:spock-core
, org.spockframework:spock-spring
).github/workflows/docs.yml
actions/checkout v4
actions/setup-java v4
actions/checkout v4
gradle/gradle-build-action v3
grails/github-pages-deploy-action v2
.github/workflows/gradle.yml
actions/checkout v4@8ade135a41bc03ea155e62e844d188df1ea18608
actions/setup-java v4
gradle/gradle-build-action v3
gradle/gradle-build-action v3
scacap/action-surefire-report v1
gradle/gradle-build-action v3
gradle/gradle-build-action v3
.github/workflows/groovy-joint-workflow.yml
actions/setup-java v4
actions/cache v4
gradle/gradle-build-action v3
actions/checkout v4
actions/setup-java v4
actions/cache v4
gradle/gradle-build-action v3
.github/workflows/release-notes.yml
actions/checkout v4
release-drafter/release-drafter v6
ncipollo/release-action v1
.github/workflows/release.yml
actions/checkout v4
actions/setup-java v4
gradle/gradle-build-action v3
actions/upload-artifact v4
gradle/gradle-build-action v3
actions/checkout v4
actions/setup-java v4
actions/checkout v4
gradle/gradle-build-action v3
actions/setup-java v4
actions/checkout v4
gradle/gradle-build-action v3
grails/github-pages-deploy-action v2
.github/workflows/retry-release.yml
actions/checkout v4@8ade135a41bc03ea155e62e844d188df1ea18608
actions/setup-java v4
gradle/gradle-build-action v3
gradle/gradle-build-action v3
gradle.properties
org.grails:grails-gradle-plugin 6.1.2
org.grails.plugins:views-gradle 3.2.3
com.github.javaparser:javaparser-core 3.25.10
javax.servlet:javax.servlet-api 4.0.1
org.codehaus.groovy:groovy-test-junit5 3.0.21
org.junit.jupiter:junit-jupiter-api 5.10.2
org.junit.platform:junit-platform-runner 1.10.2
org.junit.jupiter:junit-jupiter-engine 5.10.2
org.grails:grails-plugin-domain-class 6.2.0
org.grails:grails-datastore-gorm-test 8.1.2
org.springframework:spring-test 5.3.33
org.grails:grails-plugin-codecs 6.2.0
org.grails.plugins:async 5.0.2
org.grails:grails-datastore-gorm 8.1.2
org.grails:grails-test 6.2.0
org.springframework.boot:spring-boot-test 2.7.18
org.spockframework:spock-spring 2.1-groovy-3.0
org.spockframework:spock-core 2.1-groovy-3.0
org.grails.plugins:gsp 6.2.1
org.grails:grails-plugin-rest 6.2.0
org.grails:grails-plugin-interceptors 6.2.0
org.javassist:javassist 3.30.2-GA
settings.gradle
com.gradle.enterprise 3.16.2
com.gradle.common-custom-user-data-gradle-plugin 2.0
build.gradle
io.github.gradle-nexus:publish-plugin 2.0.0
org.asciidoctor:asciidoctor-gradle-jvm 4.0.2
grails-gorm-testing-support/build.gradle
grails-testing-support/build.gradle
grails-web-testing-support/build.gradle
gradle/wrapper/gradle-wrapper.properties
gradle 7.6.4
When writing an Interceptor test, the first test does not actually call the before method in the interceptor at all even if you have a matchAll() in the constructor.
All subsequent tests after the first appear to work fine. It's only the first one that doesn't work. It seems that the bean isn't correctly registered in the application context and the call to: applicationContext.getBeansOfType(Interceptor).values() as Interceptor[]
in the mockInterceptor method of InterceptorUnitTest returns an empty list.
I've attached a test project showing the behavior.
InterceptorTestBug.zip
dataStore is camel case in DataTest when is all lower case everywhere else in gorm.
gets a bit confusing.
getDataStore here should be renamed to getDatastore() here
https://github.com/grails/grails-testing-support/blob/master/grails-gorm-testing-support/src/main/groovy/grails/testing/gorm/DataTest.groovy#L105
I couldnt find any docs for HibernateSpec, not sure if already exists some where
Hello, I have troubles running tests for a controller similar to this one, observe that TokenService
is injected in BackofficeController
and NotificationService
.
class BackofficeController {
// TokenService is used by this controller on other methods not shown here
TokenService tokenService
NotificationService notificationService
def index() { }
def sendInvitationConfirm() {
List<Token> tokenList = Token.findAllByEnabledAndSent(true, false)
tokenList.each {Token token ->
notificationService.sendInvitation(token)
}
redirect(action:'index')
}
}
class NotificationService {
MailService mailService
TokenService tokenService
void send(String emailTo, String subjectLine, String layout, Map model = [:]) {
mailService.sendMail {
...
}
}
void sendInvitation(Token token) {
send(token.email, INVITATION_SUBJECT, 'invitation', [token:token.token])
token.sent = true
tokenService.save(token) // <-- Problematic line
}
}
When running the following test I get a Null Pointer Exception on the "problematic line" of NotificationService
.
class BackofficeControllerSpec extends Specification
implements ControllerUnitTest<BackofficeController>, DataTest {
Closure doWithSpring() {{ ->
tokenService(TokenService)
notificationService(NotificationService)
}}
def setup() {
mockDomain Token
controller.notificationService.mailService = Mock(MailService)
}
void "Problematic Test"() {
setup: "Set request method"
request.method = 'POST'
and: "Create valid token"
new Token(email:'[email protected]', token:'A' * 32).save(flush:true)
when: "Calling the action"
controller.sendInvitationConfirm()
then: "Token is updated"
Token.first().sent
and: "User is redirected"
response.redirectedUrl == '/backoffice/index'
}
java.lang.NullPointerException: Cannot invoke method save() on null object
It is my understanding that by defining the beans in doWithSrping
, the injections of TokenService
should succeed. However, it seems the injection of BackofficeController.tokenService
is successful (tested for other methods of BackofficeController
) but the injection for NotificationService.tokenService
is not.
The only way I've been able to fix my test is adding the following line to setup
in the test
controller.notificationService.tokenService = new TokenService()
This project is running on Grails 3.3.2 with the following (default) gradle configuration:
dependencies {
testCompile "org.grails:grails-gorm-testing-support"
testCompile "org.grails:grails-web-testing-support"
}
Am I missing something about service injection?
Overview: If a URL mapping is prefixed with an HTTP method, e.g., get "/my/mapping"
, then tests using assertReverseUrlMapping
or verifyReverseUrlMapping
will not be able to generate the correct URL, resulting in failure of the tests.
Example UrlMappings.groovy
(assuming a TestController
with an index
action exists):
class UrlMappings {
static mappings = {
get "/custom/mapping"(controller: 'test', action: 'index')
}
}
Example unit test/s:
class UrlMappingsSpec extends Specification implements UrlMappingsUnitTest<UrlMappings> {
void setup() {
mockController(TestController)
}
void "test forward"() {
expect: "calls to /custom/mapping to succeed" // <-- passes
verifyForwardUrlMapping("/custom/mapping", controller: 'test', action: 'index')
}
void "test reverse"() {
expect: "calls to /custom/mapping to succeed" // <-- fails
verifyReverseUrlMapping("/custom/mapping", controller: 'test', action: 'index')
}
void "test both"() {
expect: "calls to /custom/mapping to succeed" //< -- fails (due to reverse failure)
verifyUrlMapping("/custom/mapping", controller: 'test', action: 'index')
}
}
The "test forward"
test will pass, the other two will fail. Debugging indicates that the URL returned from UrlCreator.createRelativeURL
is test/index
(apparently falling back to the default URL mapping with controller name/action name).
Sample project here. Run grails test-app
to execute the unit tests in UrlMappingsSpec
.
When trying to test a grails service which uses JSON
converter throws error.
Exception message is : org.grails.web.converters.exceptions.ConverterException: Unconvertable Object of class: java.util.LinkedHashMap
Hi,
the documentation on the website is missing the code segments.
https://testing.grails.org/latest/guide/index.html#unitTestingServices
This transformation references the JUnit 4 class RunWith which has been replaced with @ExtendWith
This requires the JUnit 4 libraries to still be available in order to run integration tests in Grails 5.
create-controller
and create-domain-class
. Are they scripts in the installation, or have they changed? Where are these checked in and how do we add to development.After upgrading an app from Grails 3.2.x to 3.3.x I noticed that integration tests take a lot longer to run.
The old test plugin has a dependency on spring-boot-starter-test which includes mockito-core. Since the new testing framework does not have that dependency, mockito isn't included and it seems as though that is the source of the slowdown. Honestly I have no idea why including mockito-core would affect integration tests, but it does.
To illustrate the difference in performance, an empty Grails 3.3 app runs the following integration test roughly 3x faster if mockito-core is included as a dependency:
@Unroll
void "this test runs faster with mockito"() {
expect:
1
where:
a << Arrays.asList(new Integer[100]) // run test 100 times
}
Here's a sample project: https://github.com/dpcasady/integration-test-perf
The sample project has a testCompile dependency on mockito-core that is commented out. Run the integration tests to get a sense of the execution time, then uncomment the mockito dependency and run the tests again.
I'm not sure if the new testing framework should include mockito by default, I just thought such a big difference in test execution time should be noted.
grails:
gorm:
multiTenancy:
mode: DATABASE
tenantResolverClass: org.grails.datastore.mapping.multitenancy.web.SessionTenantResolver
class Book implements MultiTenant<Book> {
String title
}
class BookSpec extends Specification implements DomainUnitTest<Book> {
void "test something"() {
expect:"fix me"
true == true
}
}
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.grails.beans.ConstraintsEvaluator': Cannot resolve reference to bean 'grailsDomainClassMappingContext' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'grailsDatastore': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.datastore.mapping.simple.SimpleMapDatastore]: Constructor threw exception; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.lang.Class<?>]
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:648)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.grails.testing.GrailsUnitTest$Trait$Helper.defineBeans(GrailsUnitTest.groovy:100)
at org.grails.testing.gorm.spock.DataTestSetupSpecInterceptor.setupDataTestBeans(DataTestSetupSpecInterceptor.groovy:48)
at org.grails.testing.gorm.spock.DataTestSetupSpecInterceptor.configureDataTest(DataTestSetupSpecInterceptor.groovy:75)
at org.grails.testing.gorm.spock.DataTestSetupSpecInterceptor.intercept(DataTestSetupSpecInterceptor.groovy:41)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'grailsDatastore': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.datastore.mapping.simple.SimpleMapDatastore]: Constructor threw exception; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.lang.Class<?>]
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:279)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:372)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1173)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1067)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 22 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.datastore.mapping.simple.SimpleMapDatastore]: Constructor threw exception; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.lang.Class<?>]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:154)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:122)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:271)
... 40 more
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.lang.Class<?>]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:324)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:206)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:187)
at org.springframework.core.env.AbstractPropertyResolver.convertValueIfNecessary(AbstractPropertyResolver.java:266)
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:87)
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:66)
at org.springframework.core.env.AbstractPropertyResolver.getProperty(AbstractPropertyResolver.java:169)
at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:542)
at org.grails.datastore.mapping.config.ConfigurationBuilder.buildRecurse(ConfigurationBuilder.groovy:290)
at org.grails.datastore.mapping.config.ConfigurationBuilder.buildRecurse(ConfigurationBuilder.groovy:235)
at org.grails.datastore.mapping.config.ConfigurationBuilder.buildInternal(ConfigurationBuilder.groovy:111)
at org.grails.datastore.mapping.config.ConfigurationBuilder.build(ConfigurationBuilder.groovy:92)
at org.grails.datastore.mapping.core.connections.AbstractConnectionSourceFactory.create(AbstractConnectionSourceFactory.java:45)
at org.grails.datastore.mapping.core.connections.ConnectionSourcesInitializer.create(ConnectionSourcesInitializer.groovy:24)
at org.grails.datastore.mapping.simple.SimpleMapDatastore.<init>(SimpleMapDatastore.java:117)
at org.grails.datastore.mapping.simple.SimpleMapDatastore.<init>(SimpleMapDatastore.java:178)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:142)
I expected test without errors
I get BeanCreationException error
given a unit test (DataTest
, ServiceUnitTest
), after updating domain objects (i.e. persistent entity instances) in a @Transactional
service, subsequent queries produce inconsistent/wrong results with instances still having the old/pre-update values.
DummySpec
)Condition failed with Exception:
dummyWithNewNameFromService.name == "updatedDummyExplicit"
| |
null java.lang.NullPointerException: Cannot get property 'name' on null object
at myapp.DummySpec.test find after update with explicit flush from service_closure1(DummySpec.groovy:30)
at myapp.DummySpec.test find after update with explicit flush from service_closure1(DummySpec.groovy)
at groovy.lang.Closure.call(Closure.java:418)
at spock.lang.Specification.verifyAll(Specification.java:223)
at myapp.DummySpec.test find after update with explicit flush from service(DummySpec.groovy:25)
Caused by: java.lang.NullPointerException: Cannot get property 'name' on null object
... 5 more
Condition not satisfied:
!dummyWithOldName
||
|myapp.Dummy : 1
false
at myapp.DummySpec.test find after update with implicit flush from service_closure2(DummySpec.groovy:47)
at myapp.DummySpec.test find after update with implicit flush from service_closure2(DummySpec.groovy)
at groovy.lang.Closure.call(Closure.java:418)
at spock.lang.Specification.verifyAll(Specification.java:223)
at myapp.DummySpec.test find after update with implicit flush from service(DummySpec.groovy:45)
Condition failed with Exception:
dummyWithNewName.name == "updatedDummyImplicit"
| |
null java.lang.NullPointerException: Cannot get property 'name' on null object
at myapp.DummySpec.test find after update with implicit flush from service_closure2(DummySpec.groovy:48)
at myapp.DummySpec.test find after update with implicit flush from service_closure2(DummySpec.groovy)
at groovy.lang.Closure.call(Closure.java:418)
at spock.lang.Specification.verifyAll(Specification.java:223)
at myapp.DummySpec.test find after update with implicit flush from service(DummySpec.groovy:45)
Caused by: java.lang.NullPointerException: Cannot get property 'name' on null object
... 5 more
Condition failed with Exception:
dummyWithNewNameFromService.name == "updatedDummyImplicit"
| |
null java.lang.NullPointerException: Cannot get property 'name' on null object
at myapp.DummySpec.test find after update with implicit flush from service_closure2(DummySpec.groovy:50)
at myapp.DummySpec.test find after update with implicit flush from service_closure2(DummySpec.groovy)
at groovy.lang.Closure.call(Closure.java:418)
at spock.lang.Specification.verifyAll(Specification.java:223)
at myapp.DummySpec.test find after update with implicit flush from service(DummySpec.groovy:45)
Caused by: java.lang.NullPointerException: Cannot get property 'name' on null object
... 5 more
a simplified sample app will be referenced in a minute, see DummySpec
(and the different failues for each of the two test methods).
if there is anything i can do to help/speed-up fixing this, just gimme a ping.
The repo at https://github.com/jeffbrown/privatemethodissue contains the following contains the following:
package privatemethodissue
class DemoController {
DemoService demoService
def index() {}
String somePublicMethod() {
demoService.someString
}
protected String someProtectedMethod() {
demoService.someString
}
private String somePrivateMethod() {
demoService.someString
}
}
package privatemethodissue
class DemoService {
String getSomeString() {
'Some String'
}
}
The following test will fail for the non-public methods because the service instance is null in the controller methods. The public method passes.
package privatemethodissue
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class DemoControllerSpec extends Specification implements ControllerUnitTest<DemoController> {
def setup() {
controller.demoService = Mock(DemoService) {
getSomeString() >> 'Some Mock String'
}
}
void "test using a public helper method"() {
expect:
controller.somePublicMethod() == 'Some Mock String'
}
void "test using a protected helper method"() {
expect:
controller.someProtectedMethod() == 'Some Mock String'
}
void "test using a private helper method"() {
expect:
controller.somePrivateMethod() == 'Some Mock String'
}
}
Disabling the controller proxy resolves the issue. The following tests pass:
package privatemethodissue
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class DemoControllerWithNoProxySpec extends Specification implements ControllerUnitTest<DemoController> {
def setup() {
controller.demoService = Mock(DemoService) {
getSomeString() >> 'Some Mock String'
}
}
void "test using a public helper method"() {
expect:
controller.somePublicMethod() == 'Some Mock String'
}
void "test using a protected helper method"() {
expect:
controller.someProtectedMethod() == 'Some Mock String'
}
void "test using a private helper method"() {
expect:
controller.somePrivateMethod() == 'Some Mock String'
}
boolean disableControllerProxy() {
true
}
}
I don't think disableControllerProxy()
is mentioned anywhere in the docs.
HibernateSpec and DataTest are incompatible as they both create two different kind of datastore. As a results, it is impossible to have a unit test extending HibernateSpec and that test code using the mapping context.
It also results in some features that are not working as expected. For example the @transactional annotation on unit test doesnt work anymore if you implement DataTest, since GORM doesn't know which datastore to choose.
You can see a sample project with those cases here : https://github.com/anthonyMoreira/bug-hibernatespec
The integration between HibernateSpec and DomainUnitTest/ServiceUnitTest/DataTest should be transparent for the developpers and/or well documented.
With an interceptor like this:
class ActionNameInterceptor {
boolean before() {
println "$controllerName $actionName $params"
if(params.test == 'is set') {
request.setAttribute('paramSet', 'param set')
}
return true
}
}
When running, the params
is set, but when running tests, the params
is not set.
See https://github.com/sbglasius/grailsInterceptorBugs interceptorbug.ActionNameInterceptorSpec#Test interceptor params should be set
Hi
This is an update to the #54 (I will close that one, the old one was using deprecated classes)
I am using grails 3.3.10. I think I found a bug when I try to test shared constraints in the command objects.
The constraint is not available to the command object and it throws an exception.
Using the shared constraints in a domain works well as expected.
The domain
class User {
String firstName
static constraints = {
firstName shared: 'nonNullableString'
}
static mapping = {
table '`user`'
}
}
The command object inside a controller. This is my example
class AdminUserController {
def save(UserCommand command) {
if (command.hasErrors()) {
render view: 'create', model: [command: command]
return
}
new User(firstName: command.firstName).save()
flash.success = message(code: 'default.created.message')
redirect action: 'list'
}
}
class UserCommand implements Validateable{
String firstName
static constraints = {
firstName shared: 'nonNullableString'
}
}
Than add the constraints in the application.groovy
grails.gorm.default.constraints = {
nonNullableString(blank: false, maxSize: 255)
}
And then test
void testUserCommand() {
when:
UserCommand command = new UserCommand()
then:
!command.validate()
command.errors['firstName'].code == 'nullable'
}
I expect the test to run
The command object does not have the constraints during the test. I throws this exception
!command.validate()
| |
| grails.gorm.validation.exceptions.ValidationConfigurationException: Property [commandtests.UserCommand.firstName] references shared constraint [nonNullableString:null], which doesn't exist!
commandtests.UserCommand@1142f026
MacOS 10.14.4
Grails 3.3.10
JDK 1.8
I created a simple project with the problem. It is available in this repository
https://github.com/almejo/commandtest
Assertions like
assertForwardUrlMapping("/", view: 'index')
Won't work with JSON views. This is because UrlMappingsUnitTest
defaults to GSP
Iam using spock in my grails project. I wan to run the test classes in a sequence order. When I run all the test cases in a package it runs randomly. I am using linux os. How can we run the test classes in a sequence order. Or How to define test suit in spock .
I couldnt find documentation for HibernateSpec anywhere other then some examples in grails guides, if i am not missing some thing. I think it used to be available in grails reference docs earlier.
Please have HibernateSpec documented, either in gorm docs, or in grails testing docs.
i initially created issue but in wrong place grails/grails-core#10888
while doing test i have error :
org.grails.datastore.mapping.core.exceptions.ConfigurationException: Invalid connection [myDataSource] configured for class [class joe.Test]
I spent already days on this, cannot find correct solution.
One idea was to overwrite method getDatastore bdbogjoe/unit-test-multiple-datasource#2 but in some case does not work and don't think it is correct way to do this.
Do you have any idea how to fix this ? Was working fine in grails 3.1.x
For unit tests, using validate(deepValidate:false)
on already persisted domains, sometimes return an incorrect result.
For example, for the following domain
class Example {
Boolean alpha
Boolean beta
String sigma
static constraints = {
isValid notEqual:false
sigma notEqual:'bad'
}
Boolean getIsValid() {
alpha || beta
}
}
These tests fail as unit tests (because validate
returns true) but pass as integration tests
void "Test isValid for saved instance"() {
given:
new Example(alpha:true, beta:true, sigma:'good').save(flush:true)
assert Example.count() == 1
when:
Example example = Example.first()
example.alpha = false
example.beta = false
then:
!example.validate(deepValidate:false)
example.errors.getFieldError('isValid').code == 'notEqual'
}
void "Test sigma for saved instance"() {
given:
new Example(alpha:true, beta:true, sigma:'good').save(flush:true)
assert Example.count() == 1
when:
Example example = Example.first()
example.sigma = 'bad'
then:
!example.validate(deepValidate:false)
example.errors.getFieldError('sigma').code == 'notEqual'
}
You will find the relevant tests in the Grails 3.3.8 sample project, in the files ExampleSpec.groovy
and ExampleIntegrationSpec.groovy
strange_validation.zip
Thanks in advance
I'm not sure whether this is an actual issue we can improve, or simply something we can clarify in the documentation.
The issue is that with a standard mocking from DomainUnitTest
or DataTest
, some GORM features are not working, like the default sort order defined in mapping{}
. I think the reason why is because a MongoDatastore
is required instead of SimpleMapDatastore
. But maybe the reason is different.
In order for such GORM form MongoDB features to work, one needs to extend from grails.test.mongodb.MongoSpec
, and forget about DomainUnitTest
or DataTest
mocking.
Hi
I am migrating a 2.5.4 project to grails 3.3.10 and I use the compatibility layer to use my old test. I think I found a bug when I try to test shared constraints in the command objects.
The constraint is not available to the command object and it throws an exception.
Using the shared constraints in a domain works well as expected.
I also tried to use the new way of writing test files, with the ControllerUnitTest
but the result is the same
Create a command object inside a controller. This is my example
class AdminUserController {
def save(UserCommand command) {
if (command.hasErrors()) {
render view: 'create', model: [command: command]
return
}
new User(firstName: command.firstName).save()
flash.success = message(code: 'default.created.message')
redirect action: 'list'
}
}
class UserCommand implements Validateable{
String firstName
static constraints = {
firstName shared: 'nonNullableString'
}
}
Than add the constraints in the application.groovy
grails.gorm.default.constraints = {
nonNullableString(blank: false, maxSize: 255)
}
And then test
@TestFor(AdminUserController)
@Mock([User])
class AdminUserControllerSpec extends Specification {
void testUserCommand() {
when:
UserCommand command = new UserCommand()
then:
!command.validate()
command.errors['firstName'].code == 'nullable'
}
I expect the test to run as in grails 2.5.4
The command object does not have the constraints during the test. I throws this exception
!command.validate()
| |
| grails.gorm.validation.exceptions.ValidationConfigurationException: Property [commandtests.UserCommand.firstName] references shared constraint [nonNullableString:null], which doesn't exist!
commandtests.UserCommand@1142f026
I created a simple project with the problem. It is available in this repository
https://github.com/almejo/commandtest
grails version: 3.3.11
Given a controller unit test that have a service injected with an autowired annotation It throws an exception NoSuchBeanDefinitionException
.
I've reproduced the issue on a repository. It's been created with grails version 3.3.11, an item class domain and some code generated with scaffolding. I've left the test working and made the change on ItemController injecting the service with autowired on the last commit.
Git Repository with an example: https://github.com/xavier-gallofre/grails-test-autowired-issue
es.xagani.grailsTestAutowired.ItemControllerSpec > Test the index action returns the correct model FAILED
org.springframework.beans.factory.UnsatisfiedDependencyException at ItemControllerSpec.groovy:18
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException at ItemControllerSpec.groovy:18
I'm writing an integration test to test my "JSON" (in the real app it is) API.
I have a simple domain class User { String email }
and a controller that checks for duplicate emails before creation. I have two integration tests that both do the same thing, make a GET request and check response.status == 201
and User.count == 1
.
Currently, I have these two tests in separate files, UserSpec
and UserSpecAgain
. You could put both tests in the same class and it will cause the same issue. For me, UserSpecAgain
runs first and it passes fine, and then UserSpec
runs, but the domain wasn't cleared so that test fails due to a duplicate email.
I have the @Rollback
annotation on both my test specs, but it doesn't seem anything gets rolled back.
These integration tests never worked right for me (even on the old framework). I thought they were just like unit tests, but without the need to mock anything, etc. Maybe I was wrong on my assumption. Functional tests seem to lack good documentation so I'm just piecing stuff together.
Some of the extensions provided in this library are causing specs to execute methods annotated with JUnit5's @BeforeEach
but not @AfterEach.
I'm not sure if this library is trying to emulate the @BeforeEach
or if it's just something leftover, but the current behavior is not super useful.
Critically, the ordering of the calls is different than in Grails 4.0, i.e it used to be:
beforeEach
setup
test method
cleanup
afterEach
Now the ordering is:
setup
beforeEach
test method
cleanup
This makes it difficult to use @BeforeEach
in a trait or base class as usually you will want that to be called first (which it used to be). Also, Spock makes it difficult since traits can't use setup()
if the test class also has a setup method.
IMO, this library should implement an extension that creates compatibility with Before* and After* annotations while keeping the test order consistent with pre-5.0 behavior (and what would be considered the sensical ordering)
package test
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import spock.lang.Specification
class TestAnnotationSpec extends Specification {
def "test method 1"() {
when:
println "test method 1"
then:
true
}
@BeforeEach
void beforeEach() {
println "beforeEach"
}
@AfterEach
void afterEach() {
println "afterEach"
}
void setup() {
println "setup"
}
void cleanup() {
println "cleanup"
}
}
Grails team,
I'm currently writing unit test where I'm verifying a Camunda BPM process definition is correctly configured. The application defines multiple beans for different purposes including the multiple ones you need to make use of Camunda API declare in resources.groovy
A typical application resources.groovy
would look like this:
beans = {
//Other bean deinition
userPasswordEncoderListener(UserPasswordEncoderListener)
authenticationEntryPoint(Http401AuthenticationEntryPoint, 'Bearer')
logoutSuccessHandler(HttpStatusReturningLogoutSuccessHandler, HttpStatus.NO_CONTENT)
//Camunda spring bean configuration
identityProviderSessionFactory(SpringSecurityCorePluginSessionFactory)
processEngineConfiguration(SpringProcessEngineConfiguration){
processEngineName = "camunda-engine-${appName}-${Environment.currentEnvironment}"
dataSource = ref("dataSource")
databaseType = databaseUsing
databaseSchemaUpdate = camundaSchemaStrategy
transactionManager = ref("transactionManager")
history = ProcessEngineConfiguration.HISTORY_DEFAULT
jobExecutorActivate = jobExecutorEnabled
customSessionFactories = [ref("identityProviderSessionFactory")]
}
processEngine(ProcessEngineFactoryBean){ bean ->
processEngineConfiguration = ref("processEngineConfiguration")
}
runtimeService(processEngine: "getRuntimeService")
taskService(processEngine: "getTaskService")
identityService(processEngine: "getIdentityService")
historyService(processEngine: "getHistoryService")
formService(processEngine: "getFormService")
managementService(processEngine: "getManagementService")
filterService(processEngine: "getFilterService")
externalTaskService(processEngine: "getExternalTaskService")
caseService(processEngine: "getCaseService")
decisionService(processEngine: "getDecisionService")
repositoryService(processEngine: "getRepositoryService")
}
As you may see Camunda, requires a considerable amount of beans/code. Although you can create separated spring bean definition(doWithSpring()
, xml, @Config
) for your unit test to declare only beans necessary for your test spec, it is better to use the same source to run the application(resources.groovy
) and your test meeting with the DRY principle but the problem is that loadExternalBeanDefinion()
loads all beans when for the purpose of testing, you only need to load a subset of what it is declared(in my case only Camunda Beans).
Is there any way to enhance GrailsUnitTest
trait and/or GrailsApplicationBuilder
class in order to enable a bean inclusion and/or exclusion Set
similar to the plugin load feature already offered by Grails testing support?
For example:
class ProcessDefinitionSpec extends Specification implements AutowiredTest {
Closure doWithSpring() {{ ->
taskListener1(InstanceFactoryBean, Mock(ApprovalTaskListener))
}}
boolean loadExternalBeans() {true }
Set<String> excludingBeans(){
["userPasswordEncoderListener", "authenticationEntryPoint", "logoutSuccessHandler"]. toSet()
}
// ALL TEST BELOW HERE
}
or using an inclusion Set
:
class ProcessDefinitionSpec extends Specification implements AutowiredTest {
Closure doWithSpring() {{ ->
taskListener1(InstanceFactoryBean, Mock(ApprovalTaskListener))
}}
boolean loadExternalBeans() {true }
Set<String> includinglBeans(){
["identityProviderSessionFactory", "processEngineConfiguration", "processEngine", "runtimeService", "taskService", "repositoryService"]. toSet()
}
// ALL TEST BELOW HERE
}
I hope I explain the use case well!
Saludos
Hugo
Create a project: grails create-app blankProject
The project is created, go to /blankProject
Create a integration test: grails create-integration-test blankIntegrationTest
As default, this test is created:
void "test something"() {
expect:"fix me"
true == false
}
Try to run the new test: grails test-app -integration
The result tells all tests passed. But actually the test was not executed.
To see what really happened, execute the specific test:
grails test-app BlankIntegrationTestSpec --stacktrace
The test should execute and fail.
The test do not execute, due to the error below:
$ grails test-app BlankIntegrationTestSpec --stacktrace
Picked up JAVA_TOOL_OPTIONS: -Dprism.order=sw -Xms256m -Xmx2048m -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=25
> Task :integrationTest FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':integrationTest'.
> No tests found for given includes: [BlankIntegrationTestSpec](--tests filter)
* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':integrationTest'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:95)
at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:91)
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:119)
at org.gradle.api.internal.tasks.execution.ResolvePreviousStateExecuter.execute(ResolvePreviousStateExecuter.java:43)
at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:93)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:45)
at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:94)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:56)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:67)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:49)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:315)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:305)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:101)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:49)
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:43)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:134)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:129)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:202)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:193)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:129)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: org.gradle.api.tasks.testing.TestExecutionException: No tests found for given includes: [BlankIntegrationTestSpec](--tests filter)
at org.gradle.api.internal.tasks.testing.NoMatchingTestsReporter.afterSuite(NoMatchingTestsReporter.java:37)
at jdk.internal.reflect.GeneratedMethodAccessor168.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:42)
at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:230)
at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:149)
at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:58)
at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:324)
at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:234)
at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:140)
at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy68.afterSuite(Unknown Source)
at org.gradle.api.internal.tasks.testing.results.TestListenerAdapter.completed(TestListenerAdapter.java:48)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:42)
at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:230)
at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:149)
at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:58)
at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:324)
at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:234)
at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:140)
at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy70.completed(Unknown Source)
at org.gradle.api.internal.tasks.testing.results.StateTrackingTestResultProcessor.completed(StateTrackingTestResultProcessor.java:96)
at org.gradle.api.internal.tasks.testing.results.AttachParentTestResultProcessor.completed(AttachParentTestResultProcessor.java:56)
at org.gradle.api.internal.tasks.testing.processors.TestMainAction.run(TestMainAction.java:60)
at org.gradle.api.internal.tasks.testing.detection.DefaultTestExecuter.execute(DefaultTestExecuter.java:116)
at org.gradle.api.internal.tasks.testing.detection.DefaultTestExecuter.execute(DefaultTestExecuter.java:51)
at org.gradle.api.tasks.testing.AbstractTestTask.executeTests(AbstractTestTask.java:483)
at org.gradle.api.tasks.testing.Test.executeTests(Test.java:587)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:48)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:41)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28)
at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:704)
at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:671)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$2.run(ExecuteActionsTaskExecuter.java:284)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:301)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:293)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:273)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:258)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$200(ExecuteActionsTaskExecuter.java:67)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:145)
at org.gradle.internal.execution.impl.steps.ExecuteStep.execute(ExecuteStep.java:49)
at org.gradle.internal.execution.impl.steps.CancelExecutionStep.execute(CancelExecutionStep.java:34)
at org.gradle.internal.execution.impl.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:69)
at org.gradle.internal.execution.impl.steps.TimeoutStep.execute(TimeoutStep.java:49)
at org.gradle.internal.execution.impl.steps.CatchExceptionStep.execute(CatchExceptionStep.java:33)
at org.gradle.internal.execution.impl.steps.CreateOutputsStep.execute(CreateOutputsStep.java:50)
at org.gradle.internal.execution.impl.steps.SnapshotOutputStep.execute(SnapshotOutputStep.java:43)
at org.gradle.internal.execution.impl.steps.SnapshotOutputStep.execute(SnapshotOutputStep.java:29)
at org.gradle.internal.execution.impl.steps.CacheStep.executeWithoutCache(CacheStep.java:134)
at org.gradle.internal.execution.impl.steps.CacheStep.lambda$execute$3(CacheStep.java:83)
at org.gradle.internal.execution.impl.steps.CacheStep.execute(CacheStep.java:82)
at org.gradle.internal.execution.impl.steps.CacheStep.execute(CacheStep.java:36)
at org.gradle.internal.execution.impl.steps.PrepareCachingStep.execute(PrepareCachingStep.java:33)
at org.gradle.internal.execution.impl.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:38)
at org.gradle.internal.execution.impl.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:23)
at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:96)
at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:89)
at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:52)
at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:36)
at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:34)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:91)
... 32 more
* Get more help at https://help.gradle.org
BUILD FAILED in 1s
9 actionable tasks: 3 executed, 6 up-to-date
| Tests FAILED Test execution failed
You used to be able to do:
@TestFor(Book)
class BookSpec extends Specification {
def "validate is not nullable"() {
when:
domain.name = null
def result = domain.validate(['name'])
then:
!result
}
}
DomainUnitTest
does not instantiate domain property as old @TestFor
annotation did.
Workaround:
class BookSpec extends Specification implements DomainUnitTest<Book> {
@Subject
Book domain
def setup() {
domain = new Book()
}
def "validate is not nullable"() {
when:
domain.name = null
def result = domain.validate(['name'])
then:
!result
}
}
Or a solution could be to implement something like
T getDomain()
as the ControllerUnitTest does:
$ grails --version
| Grails Version: 3.3.0.RC1
| Groovy Version: 2.4.12
| JVM Version: 1.8.0_121
testing_fw_model sdelamo$ grails create-app --profile=rest-api --inplace
The create-app command generates a controller such as:
package testing_fw_model
import grails.core.GrailsApplication
import grails.util.Environment
import grails.plugins.*
class ApplicationController implements PluginManagerAware {
GrailsApplication grailsApplication
GrailsPluginManager pluginManager
def index() {
[grailsApplication: grailsApplication, pluginManager: pluginManager]
}
}
The following unit test fails because model keySet is empty.
package testing_fw_model
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
class ApplicationControllerSpec extends Specification implements ControllerUnitTest<ApplicationController> {
def "index returns valid model"() {
when:
controller.index()
then:
model.keySet().size() == 2
model.keySet().contains 'grailsApplication'
model.keySet().contains 'pluginManager'
}
}
Eclipse 4.7.2
Gradle 4.6 (testCompile 'org.grails:grails-gorm-testing-support:1.1.4')
import grails.testing.gorm.DomainUnitTest
import spock.lang.Specification
import spock.lang.Unroll
class DomainSpec extends Specification implements DomainUnitTest<Domain> {
In-eclipse > Project > Clean (start a build immediately) gives error:
The type grails.testing.gorm.DomainUnitTest$Trait$FieldHelper$1 cannot be resolved. It is indirectly referenced from required .class files
I have checked internals of referenced grails-gorm-testing-support-1.1.4.jar
Is it an IDE related issue? If so, any tips on how to get rid of it?
When trying to compile master, I get the following error:
BUG! exception in phase 'semantic analysis' in source unit '/home/sbglasius/projects/grails3-plugins/grails-testing-support/grails-web-testing-support/src/main/groovy/grails/testing/web/taglib/TagLibUnitTest.groovy' ClassNode#getTypeClass for grails.testing.web.GrailsWebUnitTest is called before the type class is set
Preventing me from attempting to fix the issues I have reported.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.