I have three classes:
class B1 {
def performB1(){}
}
class B2 {
def performB2(){}
}
class A {
private B1 b1
private B2 b2
A(b1, b2){
this.b1 = b1
this.b2 = b2
}
def perfromA(){
b1.performB1()
b2.performB2()
}
}
I want to test the method performA in the class A. So I created mocks for classes B1 and B2. Here is my class:
import groovy.mock.interceptor.MockFor;
class ATest extends GroovyTestCase {
private MockFor b1Mock
private MockFor b2Mock
void setUp() {
b1Mock = new MockFor(B1)
b2Mock = new MockFor(B2)
}
void testIsEnoughSpaceOnArtifactory_failedToGetQuotaFromArtifactory(){
b1Mock.demand.with {
performB1 { println "Performing B1" }
}
b2Mock.demand.with {
performB2 {println "Performing B2"}
}
b2Mock.use {
b1Mock.use {
def a = new A(new B1(), new B2())
a.perfromA()
}
}
}
}
It works well- I verified it. It is based on this question.
However, suppose that I have a class with three dependencies. It is still clean code. It requires 3 mocks. The code would look like that:
b3Mock.use {
b2Mock.use {
b1Mock.use {
def a = new A(new B1(), new B2(), new B3())
a.perfromA()
}
}
}
It looks ridiculous and is far from being clean. Imagine that I failed to meet the goal of no more than 3 dependencies. Then my tests will look like even more ridiculous. Is there a way to verify calls on mocks without nested closures? I could use something like that (see here for reference):
b1Mock.use {
def a = new A(new B1(), b2Mock.proxyInstance(), b3Mock.proxyInstance())
a.perfromA()
}
b2Mock.expect.verify()
b3Mock.expect.verify()
Unfortunately, when I run it, I get the following error:
java.lang.NullPointerException: Cannot invoke method performB2() on null object
Is it possible to use multiple mocks in groovy without nested closures to have clean code?
the following script works fine:
import groovy.mock.interceptor.MockFor
//------ CLASSES
class B1 { def performB1(){} }
class B2 { def performB2(){} }
class B3 { def performB3(){} }
class A {
private B1 b1
private B2 b2
private B3 b3
A(b1, b2, b3){
this.b1 = b1
this.b2 = b2
this.b3 = b3
}
def perfromA(){
b1.performB1()
b2.performB2()
b3.performB3()
}
}
//------ TESTS
def b1Mock = new MockFor(B1)
def b2Mock = new MockFor(B2)
def b3Mock = new MockFor(B3)
b1Mock.demand.with {
performB1 { println "Performing B1" }
}
b2Mock.demand.with {
performB2 {println "Performing B2"}
}
b3Mock.demand.with {
performB3 {println "Performing B3"}
}
def b2inst = b2Mock.proxyInstance()
def b3inst = b3Mock.proxyInstance()
b1Mock.use {
def a = new A(new B1(), b2inst, b3inst)
a.perfromA()
}
b2Mock.verify(b2inst)
b3Mock.verify(b3inst)
and with the following function
def useAll(List<MockFor> mocks,Closure test){
Closure use4all = mocks.inject(test){ Closure testX, next->
return { next.use(testX) }
}
use4all.call()
}
possible to minimize nested use-closures to this:
useAll([b1Mock,b2Mock,b3Mock]){
def a = new A(new B1(), new B2(), new B3())
a.perfromA()
}
Related
def withLocal(Closure cl) {
def credentialsId = env.CREDENTIALS_ID
if (credentialsId) {
echo("credentials Id=${credentialsId}")
} else {
throw new Exception("credentials not setup - env.CREDENTIALS_ID")
}
}
Excepting shouldFail and ShouldPass test cases for above groovy code.
Depending on the test-framework, the code might be slightly different, but I would put it like so:
class A {
def withLocal(Closure cl) {
def credentialsId = env.CREDENTIALS_ID
if (credentialsId) {
echo("credentials Id=${credentialsId}")
} else {
throw new Exception("credentials not setup - env.CREDENTIALS_ID")
}
}
}
// Test
// test 1 should pass: check if echo is called
// mock env with some value
A.metaClass.env = [ CREDENTIALS_ID:'abz123' ]
String out
// mock echo()
A.metaClass.echo = { out = it }
A a = new A()
a.withLocal{}
assert out == 'credentials Id=abz123'
// test 2 should fail: check if exception thrown
// mock env with empty map
A.metaClass.env = [:]
a = new A()
try{
a.withLocal{}
}catch( e ){
assert e.message == 'credentials not setup - env.CREDENTIALS_ID'
}
I have this code in my pipeline, now I want to add unit test for it using spock framework, the issue is how to mock or spy Jenkins.instance and all chaining methods.
String deployPipeline = "Deploy/${projectID}/deploy-to-prod"
def lastRelease = Jenkins.instance.getItemByFullName(deployPipeline).getLastSuccessfulBuild()
String lastDeployedVersion = lastRelease.getBadgeActions().findResult {
String text = it.getText()
if (text != null && text.matches(/^Version\=/) != null) {
return text.find(/\d+\.\d+\.\d+/)
}
}
I ended up something like this
BuildBadgeAction badge1Mock = GroovyMock(BuildBadgeAction) {
_.getText() >> "Version= 1.2.3"
_.getDisplayName() >> "Version= 1.2.3"
}
BuildBadgeAction badge2Mock = GroovyMock(BuildBadgeAction) {
_.getText() >> "badge-2"
_.getDisplayName() >> "badge-2"
}
def runMock = GroovyMock(Run) {
_.getBadgeActions() >> {
return [badge1Mock, badge2Mock]
}
}
Item itemMock = GroovyMock(Item) {
_.getFullName() >> "job-1"
_.getLastSuccessfulBuild() >> {
runMock
}
}
def jenkinsInstanceMock = GroovyMock(Jenkins) {
_.getItemByFullName(_) >> { String fullName ->
itemMock
}
GroovySpy(Jenkins, global: true, useObjenesis: true) {
_.getInstance() >> jenkinsInstanceMock
}
You don't have to mock Jenkins.instance but Run for the mock object of which you define which List<BuildBadgeAction> should be returned if its getBadgeActions() is called.
You don't want to test Jenkins' call stack but what your code does with items of a list, do you?
So, you have to do something like (in JUnit 5/Mockito semi-pseudocode, I don't use Spock, but I think you get the idea):
class YourClassTest {
#InjectMocks
YourClass yourClass = new YourClass();
#Mock
Run run;
#Test
testYourMethod() {
List<BuildBadgeAction> list = new ArrayList<>();
list.add( new ... BuildBadgeAction implementation ... );
list.add( new ... BuildBadgeAction implementation ... );
...
when( run.getBadgeActions() ).thenReturn( list );
assertEquals( "...", yourClass.getLastDeployedVersion() );
}
}
For #InjectMocks to work you have to have a declaration Run run; in YourClass (or a constructor YourClass(Run run) or a setter setRun(Run run)).
And, BTW, none of the implementations of the interface BuildBadgeAction has a getText() method.
Given the following, how do I mock processMessage() using Spock, so that I can check that processBulkMessage() calls processMessage() n times, where n is the number of messages within a BulkMessage?
class BulkMessage {
List messages
}
class MyService {
def processBulkMessage(BulkMessage msg) {
msg.messages.each {subMsg->
processMessage(subMsg)
}
}
def processMessage(Message message) {
}
}
You can use spies and partial mocks (requires Spock 0.7 or newer).
After creating a spy, you can listen in on the conversation between the caller and the real object underlying the spy:
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
subscriber.receive(_) >> "ok"
Sometimes, it is desirable to both execute some code and delegate to the real method:
subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" }
In my opinion this is not a well designed solution. Tests and design walk hand in hand - I recommend this talk to investigate it better. If there's a need to check if other method was invoked on an object being under test it seems it should be moved to other object with different responsibility.
Here's how I would do it. I know how visibility works in groovy so mind the comments.
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class MessageServiceSpec extends Specification {
def 'test'() {
given:
def service = new MessageService()
def sender = GroovyMock(MessageSender)
and:
service.sender = sender
when:
service.sendMessages(['1','2','3'])
then:
3 * sender.sendMessage(_)
}
}
class MessageSender { //package access - low level
def sendMessage(String message) {
//whatever
}
}
class MessageService {
MessageSender sender //package access - low level
def sendMessages(Iterable<String> messages) {
messages.each { m -> sender.sendMessage(m) }
}
}
It does not use Spock built-in Mocking API (not sure how to partially mock an object), but this should do the trick:
class FooSpec extends Specification {
void "Test message processing"() {
given: "A Bulk Message"
BulkMessage bulk = new BulkMessage(messages: ['a', 'b', 'c'])
when: "Service is called"
def processMessageCount = 0
MyService.metaClass.processMessage { message -> processMessageCount++ }
def service = new MyService()
service.processBulkMessage(bulk)
then: "Each message is processed separately"
processMessageCount == bulk.messages.size()
}
}
For Java Spring folks testing in Spock:
constructorArgs is the way to go, but use constructor injection. Spy() will not let you set autowired fields directly.
// **Java Spring**
class A {
private ARepository aRepository;
#Autowire
public A(aRepository aRepository){
this.aRepository = aRepository;
}
public String getOne(String id) {
tryStubMe(id) // STUBBED. WILL RETURN "XXX"
...
}
public String tryStubMe(String id) {
return aRepository.findOne(id)
}
public void tryStubVoid(String id) {
aRepository.findOne(id)
}
}
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository])
when:
a.getOne()
then:
// Stub tryStubMe() on a spy
// Make it return "XXX"
// Verify it was called once
1 * a.tryStubMe("1") >> "XXX"
}
}
Spock - stubbing void method on Spy object
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository]) {
1 * tryStubVoid(_) >> {}
}
when:
...
then:
...
}
}
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 = ...).