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.
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'm rendering a GSP in a Spock unit test with Grails 2.4.4. The GSP has a number of custom taglibs, and many of them call external services. The problem is that I can't inject a stubbed service into those taglibs, and when I call the service, it's always null. Here's what I'm trying to get simplest case working:
#TestMixin(GroovyPageUnitTestMixin)
#Mock(FooTagLib)
class MyGspSpec extends Specification {
def setup() {
def barStub = Stub(BarService)
def tagLibStub = GroovyStub(FooTagLib, global:true) {
}
tagLibStub.barService = barStub
}
def 'test method'() {
when: String result = render('myView', model:[:])
then: assert result != null
}
}
Taglib:
class FooTagLib {
static String namespace = "baz"
BarService barService
Closure<StreamCharBuffer> foo = { GroovyPageAttibutess attrs ->
out << barService.something()
}
}
_foo.gsp contents:
<baz:foo/>
I've also tried this:
FooTagLib.metaClass.barService = Stub(BarService) //also tried GroovyStub
I even tried putting a getter on the taglib and stubbing that, but it didn't work either:
In setup:
def barService = Stub(BarService)
GroovyStub(FooTagLib, global:true) {
getBarService() >> barService
}
In taglib:
BarService barService
BarService getBarService() { return barService }
//....
out << getBarService().something()
Finally, the only thing that worked (with the getter) is this:
FooTagLib.metaClass.getBarService = { -> return Stub(BarService) }
But this seems like a really bad hack.
I'm not sure how I can inject a stubbed version of it into the taglib. In my mind at least one of these should work, but obviously I'm doing something wrong.
This is how I would pass unit specs for the tagLib:
#TestFor(FooTagLib)
class FooTagLibSpec extends Specification {
def setup() {
// tagLib is available by default of above annotation is used
tagLib.barService = Mock(BarService) {
// Mock the service and stub the response
1 * serviceMethod() >> { "Hello World Returns" }
}
}
void "test something"() {
expect: 'applying template would result in mocked response'
applyTemplate('<baz:foo/>') == "Hello World Returns"
}
void "test taglib method call"() {
expect:
tagLib.foo() == "Hello World Returns"
}
}
class FooTagLib {
static defaultEncodeAs = [taglib:'html']
static String namespace = "baz"
BarService barService
Closure<StreamCharBuffer> foo = { attrs ->
out << barService.serviceMethod()
}
}
class BarService {
def serviceMethod() { "Hello World" }
}
I hope I was able to meet the checkpoints you were looking for in your tests. You can also see spec is able to mock BarService without issues. Give a shout if you need for info.
I'm using Grails 2.3.8 and trying to create a unit test for a filter that uses a service.
The filter:
class LicenseFilters {
def licenseService
def filters = {
all(controller:'*', action:'*') {
before = {
if(!licenseService.checkLicense()){
redirect(controller:"licenseExpired")
return false
}
}
}
}
}
The spec, first attempt:
#TestFor(ExecutionTraceController)
#Mock(LicenseFilters)
class LicenseFiltersSpec extends Specification{
void "Test filter redirects when license is wrong"() {
given:
LicenseFilters bean=applicationContext.getBean("com.nortia.sgmentia.license.LicenseFilters")
bean.licenseService=this.buildLicenseServiceStub(false)
when:
withFilters(action:"list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
private LicenseService buildLicenseServiceStub(boolean ok){
LicenseService result=Stub(LicenseService)
result.checkLicense() >> ok
return result
}
}
But it turns out (by debugging) that the bean that I grab from the context it is NOT the same one that receives the request thus I still get a NPE.
In a second attempt I try using defineBeans:
void "Test filter redirects when license is wrong"() {
given:
defineBeans {
licenseService(MethodInvokingFactoryBean){
targetObject = this
targetMethod= "buildLicenseServiceStub"
arguments=[false]
}
}
when:
withFilters(action:"list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
But the mocked bean is neither bean instanciated nor inyected.
Should I try to inyect the service manually into the filter??
There was this issue https://jira.grails.org/browse/GRAILS-8976 but it is closed.
I came across a similar situation and was able to fix it by adding the service to the #Mock annotation, i.e. #Mock([LicenseFilters, LicenseService]).
In your case the spec would look something like the following:
#TestFor(ExecutionTraceController)
#Mock([LicenseFilters, LicenseService])
class LicenseFiltersSpec extends Specification {
void "Test filter redirects when license is wrong"() {
given:
defineBeans {
licenseService(MethodInvokingFactoryBean) {
targetObject = this
targetMethod = "buildLicenseServiceStub"
arguments = [false]
}
}
when:
withFilters(action: "list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
private LicenseService buildLicenseServiceStub(boolean ok) {
LicenseService result = Stub(LicenseService)
result.checkLicense() >> ok
return result
}
}
Note: that mocking the service in this manner will, by default, inject an instance of the actual LicenseService into your filter. So, if the above defineBeans block is removed the actual implementation of LicenseService.checkLicense() will be called.
I finally found a workaround to make it work going with the second approach (using defineBeans).
The service is not being autowired into the filter so I finally did it manually with a pseudo-singleton:
class LicenseFilters {
def licenseService
def filters = {
all(controller:'*', action:'*') {
before = {
if(!this.licenseService){
this.licenseService=applicationContext.getBean("licenseService")
}
if(!this.licenseService.checkLicense()){
redirect(controller:"licenseExpired")
return false
}
}
}
}
}
Quite ugly but a solution at least.
Hope it helps someone out there.
I am updating unit tests in a Grails project. We were originally using version 1.3.9 and now we are updating to version 2.3.9. I am using Spock.
I keep getting this error:
results:
junit.framework.AssertionFailedError: Condition not satisfied:
controller.edit() == [filterCategoryInstance: filterCategoryInstance]
| | | |
| null false John
com.xxxxxx.xxxxx.FilterCategoryController#20574000
Here is the controller code:
#Secured(["hasAnyRole('CM_ADMIN')"])
def edit() {
def filterCategoryInstance = FilterCategory.get(params.id)
if (!filterCategoryInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'dpFilterCategory.label', default: 'FilterCategory'), params.id])}"
redirect(action: "list")
}
else {
return [filterCategoryInstance: filterCategoryInstance]
}
}
and here is the test code:
#Mock([FilterCategory, FilterCategoryTag])
#TestFor(FilterCategoryController)
#TestMixin(DomainClassUnitTestMixin)
class FilterCategoryControllerSpec extends ExtendedControllerSpec {
def 'edit action: existing FilterCategory'() {
setup:
mockI18N(FilterCategoryController)
params.id = filterCategoryInstance.id
expect:
controller.edit() == [filterCategoryInstance: filterCategoryInstance]
where:
tag = new FilterCategoryTag(name: 'tag1')
filterCategoryInstance = new FilterCategory(name: "John",
submissionText:"John", sortOrder:0, 'filterCategoryTags': [tag])
}
And here is the ExtendedControllerSpec code. I hope I have included enough code:
I have looked at the following web pages for guidance:
#Mixin(MetaClassMixin)
class ExtendedControllerSpec extends Specification {
def props
protected void setup() {
//super.setup()
props = new Properties()
File file = new File("grails-app/i18n/messages.properties")
if (file.exists()) {
def stream = new FileInputStream(file)
props.load stream
stream.close()
}
mockI18N(controller)
}
def mockI18N = { controller ->
controller.metaClass.message = { Map map ->
if (!map.code)
return ""
if (map.args) {
def formatter = new MessageFormat("")
if (props.getProperty(map.code)) {
formatter.applyPattern props.getProperty(map.code)
}
return formatter.format(map.args.toArray())
} else {
if (props && props.hasProperty(map.code)) {
return props.getProperty(map.code)
} else {
return map.code
}
}
}
}
/**
* add dynamic methods in test setup.
*/
protected void addDynamicMethods() {
registerMetaClass(String)
String.metaClass.mixin StringUtils
}
protected GrailsUser mockGrailsUser() {
return Mock(GrailsUser)
}
...
/**
* must call AFTER mockDpSercurityService
*/
protected void setHasRoleTrue() {
if (controller?.dpSecurityService?.metaClass) {
controller.dpSecurityService.metaClass.hasRole = {return true}
}
}
protected void setHasRoleFalse() {
if (controller?.dpSecurityService?.metaClass) {
controller.dpSecurityService.metaClass.hasRole = {return false}
}
}
protected void mockUserService() {
controller.dpUserService = new MockFor(UserService)
}
}
http://sanjaykanwar.blogspot.com/2012/07/grails-controller-test-with-spock.html
http://naleid.com/blog/2012/05/01/upgrading-to-grails-2-unit-testing
Looks like the if branch gets executed in edit() instead of the else branch because FilterCategory does not get saved and therfore does not get a proper id.
With JustMock I can mock DataContext tables with lists in Linq to SQL easily like this, where an IEnumerable is taking the place of each DataContext's table through ReturnsCollection() allowing me to plug in fake data:
[TestMethod]
public void ShouldGetManagersByHireDate()
{
var context = Mock.Create<MyDataContext>();
Mock.Arrange(()=> context.Employees).ReturnsCollection(GetFakeEmployees());
Mock.Arrange(() => context.Managers).ReturnsCollection(GetFakeManagers());
var repository = new EmployeeRepository(context);
var managers = repository.GetManagersByHireDate(new DateTime(2002, 1, 1), DateTime.Now);
Assert.AreEqual(1, managers.Count());
Assert.AreEqual(1, managers.FirstOrDefault().ID);
}
private IEnumerable<Employee> GetFakeEmployees()
{
return new List<Employee> {
new Employee { ID = 1, HireDate = new DateTime(2004, 12, 1) },
new Employee { ID = 2, HireDate = new DateTime(2006, 7, 1) },
new Employee { ID = 3, HireDate = new DateTime(2009, 3, 1) }
};
}
private IEnumerable<Manager> GetFakeManagers()
{
return new List<Manager> {
new Manager { ID = 1 }
};
}
And this would be the method under test:
public IQueryable<Employee> GetManagersByHireDate(DateTime start, DateTime end)
{
return from e in context.Employees
join m in context.Managers on e.ID equals m.ID
where e.HireDate >= start && e.HireDate <= end
select e;
}
I am looking for some way of performing the same magic allowing me to use IEnumerable<T> in place of Table<T> for the purpose of testing Linq to SQL, preferably in FakeItEasy.
Linq to SQL isn't the easiest thing to test using open source tools. Hopefully, this approach may work for you.
FakeItEasy doesn't have a method exactly like JustMock's ReturnCollection that would allow you to mock out an ITable to return an IEnumerable. One option you have is to create a MockableTable similar to the one shown here. Then to populate your Table, you could have something like
private ITable<Employee> GetFakeEmployees() {
List<Employee> sampleData = /* fill it up with employees */
var employeeTable = new MockableTable<Employee>(null, sampleData.AsQuerable());
return employeeTable;
}
Also, FakeItEasy won't intercept the Employees property on the DataContext since it's a non virtual property on a concrete class. You could create a simple custom base class and have MyDataContext class derive directly from that.
public abstract class CustomDataContext : DataContext, ICustomDataContext {
}
public interface ICustomDataContext {
ITable<Employee> { get; }
}
The main point here is to leverage the interface for your mock. Then in your test method, you would have:
[TestMethod]
public void ShouldGetManagersByHireDate() {
var context = A.Fake<ICustomDataContext>();
A.CallTo(()=> context.Employees).Returns(GetFakeEmployees());
var repository = new EmployeeRepository(context);
var managers = repository.GetManagersByHireDate(new DateTime(2002, 1, 1), DateTime.Now);
Assert.AreEqual(1, managers.Count());
Assert.AreEqual(1, managers.FirstOrDefault().ID);
}
I haven't actually compiled this, but concept should be stable. Relying on the abstractions will make your code more testable and easier to mock.
Hope this helps somewhat.
The way I have done this using FakeItEasy is to add an interface to DataContext and use that as my dependency in my repos. E.g.
public interface IMyDataContext : IDisposable
{
IQueryable<Employee> Employees { get; }
// etc.
}
public partial class MyDataContext: IMyDataContext
{
IQueryable<Message> IMyDataContext.Employees
{
get { return this.Employees; }
}
// etc.
}
public class EmployeeRepository
{
public EmployeeRepository(IMyDataContext context)
{
// etc.
}
}
And in my test:
var context = A.Fake<IMyDataContext>();
A.CallTo(() => context.Employees).Returns(new[] { new Employee { Name = "John", Name = "Fred" }.AsQueryable());
var repository = new EmployeeRepository(context)
I don't think any considerations surrounding ITable are required.