I am attempting to write a test for a controller which calls a service method. I would like to mock a dependent method within that service.
My spec is as follows:
MyController myController = new MyController()
def mockMyService
def "My spy should be called"() {
when:
mockMyService = Spy(MyService) {
methodToSpy() >> {
println "methodToSpy called"
} // stub out content of this fn
}
myController.myService = mockMyService
myController.callService()
then:
1 * mockMyService.methodToSpy()
}
When I attempt to run this test, I get the following error:
Failure: |
My spy should be called(spybug.MyControllerSpec)
|
Too few invocations for:
1 * mockMyService.methodToSpy() (0 invocations)
Unmatched invocations (ordered by similarity):
1 * mockMyService.serviceMethod()
1 * mockMyService.invokeMethod('methodToSpy', [])
1 * mockMyService.invokeMethod('println', ['in serviceMethod about to call methodToSpy'])
1 * mockMyService.invokeMethod('println', ['Back from methodToSpy'])
As you can see, Spock is capturing the Groovy invokeMethod call, not the subsequent call to the actual method. Why is this happening?
The complete project is available here.
Try this:
def "My spy should be called"() {
given:
mockMyService = Mock(MyService)
myController.myService = mockMyService
when:
myController.callService()
then:
1 * mockMyService.methodToSpy(_) >> { println "methodToSpy called" }
}
According to the spock documentation for stubs, if you want to use the cardinality, you must use a Mock and not a Stub.
http://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html#_stubbing
Related
I am writing a Jenkins pipeline library, and am having some difficulties with mocking/validating an existing Jenkins pipeline step.
I am using jenkins-spock by homeaway to unit test, but I think my problem is more Spock related.
import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification
import com.company.pipeline.providers.BuildLogProvider
class PublishBuildLogSpec extends JenkinsPipelineSpecification {
BuildLogProvider buildLogProvider = Mock()
PublishBuildLog publishBuildLog
def setup () {
publishBuildLog = new PublishBuildLog(buildLogProvider: buildLogProvider)
explicitlyMockPipelineStep('writeFile')
}
def "Gets the log file contents for a specific job and build"() {
when:
"the call method is executed with the jobName and buildNumber parameters set"
publishBuildLog.call("JOBNAME", "42")
then:
"the getBuildLog on the buildLogProvider is called with those parameters"
1 * buildLogProvider.getBuildLog("JOBNAME", "42")
}
def "the contents of log file is written to the workspace"() {
given:
"getBuildLog returns specific contents"
def logFileText = "Example Log File Text"
buildLogProvider.getBuildLog(_, _) >> logFileText
when:
"publishBuildLog.call is executed"
publishBuildLog.call(_, _)
then:
"the specific contents is passed to the writeFile step"
1 * getPipelineMock("writeFile").call([file: _ , text: logFileText])
}
}
This is my unit test. I am attempting to say that writeFile is called with the text matching the contents of logFileText, ignoring what the other parameters are. I have tried numerous combinations, but always seem to get the same or similar response to response of:
Too few invocations for:
1 * getPipelineMock("writeFile").call([file: _ , text: "Example Log File Text"]) (0 invocations)
Unmatched invocations (ordered by similarity):
1 * (explicit) getPipelineMock("writeFile").call(['file':'filename', 'text':'Example Log File Text'])
This is to test this class
import com.company.pipeline.providers.BuildLogProvider
class PublishBuildLog {
BuildLogProvider buildLogProvider = new BuildLogProvider()
void setBuildLogProvider(BuildLogProvider buildLogProvider) {
this.buildLogProvider = buildLogProvider
}
def call(def jobName, def buildNumber) {
def contents = buildLogProvider.getBuildLog(jobName, buildNumber)
writeFile(file: "filename", text: contents)
}
}
I am at a loss as to how to validate this call. I have a lot of experience with Java and Junit, but I am relatively new to Spock.
How can I verify this?
For me your test passes. But there is one thing I find strange: You use jokers in a when: block where you should really use concrete parameters like in the first feature method:
when: "publishBuildLog.call is executed"
publishBuildLog.call(_, _)
Instead you should write:
when: "publishBuildLog.call is executed"
publishBuildLog.call("JOBNAME", "42")
For me this works just fine if I use this as a dummy class in order to make the code compile (because you did not provide the source code):
class BuildLogProvider {
def getBuildLog(def jobName, def buildNumber) {}
}
I have a GroovyMock of a static method. When my mock method is called the test fails because the correct arguments were not used, even though I accept all arguments to the mock. Why is this please?
// FileDownloadingService.groovy
class FileDownloadingService {
// I am going to mock this static method
static void download(URL urlLocation, String localDir, String localName) {
}
}
// ServiceUnderTestService.groovy
class ServiceUnderTestService {
def downloadData(URL url) {
FileDownloadingService.download(url, "temp", "ReferenceData.gz")
}
}
// within ServiceUnderTestServiceSpec
void "file is downloaded"() {
given: "A url for the file to download"
def urlLocation = "http://example.com/ReferenceData.gz"
def url = new URL(urlLocation)
def fileDownloadMock = GroovyMock(FileDownloadingService, global: true)
when: "we call downloadData"
service.downloadData(url)
then: "we actually try to download it"
1 * fileDownloadMock.download(_, _, _)
}
I get the following error message:
| Too few invocations for:
1 * fileDownloadMock.download(_, _, _) (0 invocations)
Unmatched invocations (ordered by similarity):
1 * fileDownloadMock.download(http://example.com/ReferenceData.gz, 'temp', 'ReferenceData.gz')
at org.spockframework.mock.runtime.InteractionScope.verifyInteractions(InteractionScope.java:78)
at org.spockframework.mock.runtime.MockController.leaveScope(MockController.java:76)
Why does the mock not pass, as I'm specifying all parameters are valid, no?
For a static method, the interaction needs to be specified as follows:
1 * FileDownloadingService.download(*_) // or: (_, _, _)
I am using Grails 2.4.4 and want to apply spock tests in my project.
Below is the MemberService:
#Transactional
class MemberService {
def lastMember = null
def login (userId, password) {
def member = Member.findByLoginEmail(userId)
if(!member) return LoginResult.NO_SUCH_MEMBER
if (member.isLocked) return LoginResult.MEMBER_IS_LOCKED
log.debug("member.password is [${member.passwd}], input password is [${password}]")
if (!member.passwd.equals(password)) return LoginResult.PASSWORD_ERROR
switch(member.validateResult) {
case "FAILED":
return LoginResult.VAILDATE_FAILED
case "WAIT":
return LoginResult.WAIT_VALIDATE
}
member.lasLoginTime = new Timestamp(System.currentTimeMillis())
member.lastLoginPlatform = "WEB"
member.loginFailCount = 0
member.save()
lastMember = member
return LoginResult.SUCCESS
}
enum LoginResult {
SUCCESS,
NO_SUCH_MEMBER,
PASSWORD_ERROR,
MEMBER_IS_LOCKED,
VAILDATE_FAILED,
WAIT_VALIDATE
}
enum ValidateResult {
SUCCESS,
FAILED,
WAIT
}
}
MemberServiceSpec is as below:
#TestFor(MemberService)
#Mock(Member)
class MemberServiceSpec extends Specification {
def memberService
def setup() {
memberService = new MemberService()
}
void "test something"() {
when:
println "service is ${service}"
def result = memberService.login("aa#mymail.com", "123456")
println "result is ${result}"
then:
result == MemberService.LoginResult.SUCCESS
}
}
The test result is as below:
Testing started at 15:15 ...
|Loading Grails 2.4.4
|Configuring classpath
.
|Environment set to test
....................................
|Running without daemon...
..........................................
|Compiling 1 source files
.
|Running 2 unit tests...|Running 2 unit tests... 1 of 2
--Output from test something--
1. setup
service is cusine_market.MemberService#54c425b1
Enter MemberService.login----------------userId=[aa#mymail.com]
result is NO_SUCH_MEMBER
Failure: |
test something(cusine_market.MemberServiceSpec)
|
Condition not satisfied:
result == MemberService.LoginResult.SUCCESS
| |
| false
NO_SUCH_MEMBER
at cusine_market.MemberServiceSpec.test something(MemberServiceSpec.groovy:32)
Condition not satisfied:
result == MemberService.LoginResult.SUCCESS
| |
| false
NO_SUCH_MEMBER
Condition not satisfied:
result == MemberService.LoginResult.SUCCESS
| |
| false
NO_SUCH_MEMBER
at cusine_market.MemberServiceSpec.test something(MemberServiceSpec.groovy:32)
|Completed 1 unit test, 1 failed in 0m 6s
.Tests FAILED
I confirm that the user exists in database.
Could anyone tell me why MemberService can not find the user "aa#mymail.com" in database? I also tried the below line
memberService = Mock(MemberService);
The result is the same. However, if I run-app, the service does find the user.
Mocking
In your test you're testing MemberService, which means you don't need to mock it. Just refer to it via service.
Test Data
This looks like unit test, not the integration one. So it doesn't use your database at all.
What you need to do is to create the Member instance manually in test. You have it in your #Mock annotation, which is good.
Now, create the object, preferably in a given block:
void "test something"() {
given:
new Member(loginEmail: 'aa#mymail.com', password: '123456', ...).save(failOnError: true, flush: true)
when:
println "service is ${service}"
def result = service.login("aa#mymail.com", "123456")
println "result is ${result}"
then:
result == MemberService.LoginResult.SUCCESS
}
I added failOnError: true to make sure the object has actually been created. You - of course - need to provide all required properties in Member constructor and make sure the data you provide correspond to the one you provide to login method.
Also, you need to ensure the Member object is initialized in a way that fulfills the path you want to reach. For example, if you want to reach LoginResult.SUCCES state, you need to set member.isLocked to false, etc.
BONUS
When you get this test to work, you may want to take a look into Build Test Data Plugin. It makes creating test data - like your Member object - much easier.
I am in grails 2.3.1 - trying to use the async features.
This is bulk data processing. I am trying to synchronise 2 databases, which involves comparing both and returning a list of 'deltas'. I am trying to speed up the process
The documentation says I can just add a set of closures to a PromiseList and then call onComplete() to check that all the closures have completed. These are my attempts - directly building on "You can also construct a PromiseList manually" in the documentation:
def tasksMemberDeltas = new PromiseList()
pages.each {Integer page ->
tasksMemberDeltas << {findCreateMemberDeltas(page, (page + pageSize) - 1)}
if (page % 30 == 0) {
tasksMemberDeltas.onComplete {
tasksMemberDeltas = new PromiseList()
}
}
Returns:
Error groovy.lang.MissingMethodException:
No signature of method: java.util.ArrayList.onComplete()
In the end I called .get() which calls waitAll. Going into .get() and finding that it did waitAll was my revelation.
So if I have a single task I call:
waitAll finalDeltas
If I have a list I call:
taskFinalDeltas.get()
onComplete() logically relates to a single delta. Not the list. So this works OK:
Promise memberDeleteDeltas = task {
findDeleteAndTagDeltas()
}
memberDeleteDeltas.onError { Throwable err ->
println "An error occured ${err.message}"
}
memberDeleteDeltas.onComplete { result ->
println "Completed create deltas"
}
waitAll(memberDeleteDeltas)
I am attempting to write integration tests for a Grails service that does, among other things, send email via the excellent Mail Plugin. I can disable the actual sending of email via configuration, which is great, but I would like to verify that the parameters are correct body is being rendered correctly, or at very least that the method has been called when I expect. As per the documentation, the full path to GSP for the body must be supplied.
As part my test I'd like to do something like this - is there a way to access the email body and other parameters programmatically after sending?
sendMail {
to myemailparams.to
subject myemailparams.subject
body( view:"/emailviews/someemailview",
model:[contentparam: myemailparams.somecontentvalue)
}
//verify correct sending to and subject parameters, and that body contains correct contentvalue
//or at least that the method has been called (Mock it out?)
Note I realize that I can encapsulate the testing of the email body rendering into a separate isolated test that doesn't involve the mail plugin. But the purpose of this integration test is to ensure a lot of things, including the email send, happen correctly upon call to the service method. I would even be satisfied with an answer that describes how to mock the service, and a check that verifies 'sendMail' has been called when expected.
You can override the sendMail method using metaClass and then do some checking to ensure that sendMail was called:
void testSendMail() {
MyClass myClass = new MyClass()
def sendMailCalled = false
myClass.metaClass.sendMail = { Closure c->
sendMailCalled = true
}
myClass.functionThatCallsSendMail()
assert sendMailCalled
}
Here is an example of how I assert things that were sent to MailService:
def setup(){
mailParams = [:]
mockMailService.ignore.sendMail{ callable ->
messageBuilder = new MailMessageBuilder(null, new ConfigObject())
messageBuilder.metaClass.body = { Map params ->
mailParams.body = params
}
messageBuilder.metaClass.async = { Boolean param ->
mailParams.async = param
}
messageBuilder.metaClass.to = { String param ->
mailParams.to = param
}
messageBuilder.metaClass.subject = { String param ->
mailParams.subject = param
}
callable.delegate = messageBuilder
callable.resolveStrategy = Closure.DELEGATE_FIRST
callable.call()
}
service.mailService = mockMailService.proxyInstance()
}
Then, In my test, after executing the method that sends mail, I assert this way (Spock syntax for assertions):
then:
mailParams.to == 'test#test.com'
mailParams.async == true
mailParams.subject == 'fnuser.billingEmail.subject{}en'
mailParams.body.view == '/mailtemplates/setBillingEmail'
mailParams.body.model.destinationUrl == "myDestinationUrl"
mailParams.body.model.logoUrl == 'myUrl/templatelogo.png'
mailParams.body.model.locale == Locale.ENGLISH