I've got three versions of a backend that I'm testing. I would like to run similar feature specifications against the three versions.
Initially, I thought I'd just organize everything in a directory structure, as such:
features/
v1/
something.feature
step_definitions/
something_steps.rb
v2/
something.feature
step_definitions/
something_steps.rb
v3/
something.feature
step_definitions/
something_steps.rb
However, cucumber seems to flatten everything, which means that I end up with ambiguous step definitions.
I then thought of the following structure:
features/
v1/
something.feature
v2/
something.feature
v3/
something.feature
step_definitions/
something_steps.rb
I'd define a variable in the feature file somewhere, indicating which version that one is for, and I'd have a bunch of "ifs" inside the steps file, to choose code paths depending on that version variable. However, I haven't found an obvious way of defining that variable in the feature file.
Is there any way I can organize things, or will I just have to create multiple "feature" roots, one per version, which would be an awful solution given that it would mean multiple invocations of cucumber?
v1/
features/
something.feature
step_definitions/
something_steps.rb
v2/
features/
something.feature
step_definitions/
something_steps.rb
v3/
features/
something.feature
step_definitions/
something_steps.rb
Why would multiple invocations of cucumber be a bad thing? Personally, that's how I'd do it, and run all three features from a Rakefile so I didn't have to do one after the other.
Also, if the conflicts are so large that you're rewriting the entire step definition for each version, maybe you should reconsider how you're wording them. Should you really describe it as doing the same thing if the code is so different?
If the changes are smaller, then I'd suggest looking at tags and hooks to achieve what you want.
So your feature might look like this:
#v1
Feature: some feature
In order to test different versions
As a cucumber user
I want to be able to change step definitions based on what version feature is running
Scenario: some scenario
Given some thing
When I pass some method
Then I should get something
And your step definition might look like this:
Before('#v1') do
Thing = V1Thing
VERSION = 1
end
Given /^some thing&/ do
#thing = Thing.new
end
When /^I pass some method$/ do
#thing.some_action!
end
Then /^I should get something$/
thing = "something" if VERSION == 1
thing = "something else" if VERSION == 2
#thing.prop.should == thing
end
You can use this method even if the step definitions are very different, but I think it will be harder to manage.
Related
I am trying to set a module's source (this IS NOT a resource) based on a conditional trigger but it looks like the module is getting fired before the logic is applied:
module "my_module" {
source = "${var.my_field == "" ? var.standard_repo : var.custom_repo}"
stuff...
more stuff...
}
I have created the standard_repo and custom_repo vars as well and defined with URLs for respective repos (using git:: -- this all works w/o conditional)
All this being said, anyone know of a way to implement this conditional aspect? (again, this is a module and not a resource)
I tried using duplicate modules and calling based off the var value but this, too, does not work (condition is never met, even when it is):
repo = ["${var.my_field == "na" ? module.my_module_old : module.my_module_new}"]
One way to achieve this is described in this post
Basically, a common pattern is to have several folders for different environments such as dev/tst/prd. These environments often reuse large parts of the codebase. Some may be abstracted as modules, but there is still often a large common file which is either copy-pasted or symlinked.
The post offers a way that doesn't conditionally disable based on variables but it probably solves your issue of enabling a module based on different enviornments. It makes use of the override feature of terraform and adds a infra_override.tf file. Here, it defines a different source for the module which points to an empty directory. Voila, a disabled module.
Variables are not allowed to be used in the module source parameter. There also does not seem to be a plan for this to change. https://github.com/hashicorp/terraform/issues/1439 . Creating a wrapper script , or using something like mustache http://mustache.github.io/ seems to be the best way to solve the problem.
Sorry if the title is a bit confusing, but what are the options/conventions that Origen provides for setting up subblocks that aren't necessarily silicon models, or are just general helpers?
For example, I have a scan helper plugin that guides the user through creating a scan test program. I'd like to add a list of options/customizations to the top-level app. There are a few ways to do this:
I can add a list of attr_readers/methods. I think this looks a bit ugly though and adds a bunch of stuff to the toplevel that isn't used by anything else, and it blows up $dut.methods.
I could use parameters as defined here: http://origen-sdk.org/origen/guides/models/parameters/ and just call of them in the scan tester app. But looking at the guides I don't think that is the desired use case. It looks more like context switching, but maybe that was just the example use case.
I could add a scan_tester.setup method or something on the toplevel. This just seems unnecessary though since its basically doing the same thing as #2, but requires a 'setup' method to be called. Yeah, its only 1 line, but if you mess up or forget to add that line then you've got some debug to do avoided by #2 (I can print a warning for example if the scan parameters aren't provided to help warn of typos, etc.).
I can set it up as a subblock (currently how I've got it), but this doesn't really fit. Scan isn't a silicon model, so base address is useless, but required. It has no registers, etc.
Then there's other 'Ruby' things I could do (setup via on_create, use global variable etc.) but these all seem not as great as any of the options above for one reason or another (mainly, more setup required on my part than using any of the existing options).
Any one of these would work. But from a convention standpoint, which direction should my scan tester setup go? Is there another option I hadn't considered? I'd lean towards option #2 as it looks the cleanest.
Thanks
This is a really good question.
There are actually two other options:
Add application config parameters from the plugin: http://origen-sdk.org/origen/release_notes/#v0_7_24
Define a constant as used by the JTAG and other early plugins: http://origen-sdk.org/jtag/#How_To_Use
I think #2 is using parameters in a way that was not originally intended, maybe it could work though but I just can't picture it.
I don't really like #5 or #6 since they provide application-level and class-level configuration, which is sometimes what you want, but often these days I see the need more for (DUT) instance-level configuration.
So, my best answer here is that I don't know, but you are touching on a good point that we need to have an official API or at least a recommendation for this.
I think you should be open to the possibility of adding something new to Origen for this if you can think of something better.
As I'm writing this, I suppose #5 would also support instance-level configuration, albeit a bit long-winded:
def initialize(options = {})
Origen.app.config.scan_chain_length = 6
end
My comment wouldn't keep its format, so here it is but looks better:
#Ginty
What would you think of a 'component' API. For example, we could have:
# components.rb
component(:scan, TIPScan::ScanTester,
# options
wgl_dir: ..., # defaults to Origen.app.root/pattern/wgl
custom_sort: proc do {|wgl_name| ...},
)
# then we can do things like:
$dut.scan #=> TIPScan instance
$dut.component(:scan) #=> same as above
$dut.components #=> [TIPScan instance, ...]
$dut.has_component(:scan) #=> true etc.
Pretty much just a stripped down subblock class to handle these. I think our IAR/C compilers and even CATI could benefit from this and make the setup cleaner and more customizable.
Is it possible to parameterise a feature file in the same way it is a scenario? So each scenario in the feature could refer to some variables which are later defined by a single table for the entire feature file?
All of the answers I've found so far (Feature and scenario outline name in cucumber before hook for example) use Ruby meta-programming, which doesn't inspire much hope for the jvm setup I'm using.
No its not, and for good reason. Feature files are meant to be simple and readable, they are not for programming. Even using scenario outlines and tables is generally not a good thing, so taking this further and having a feature that cannot be understood without reading some other thing that defines variables is counter productive.
You can however put all your variables and stuff in step definitions and write your feature at a higher level of abstraction. You'll find implementing this much easier, as you can use a programming language (which is good at this stuff).
One way of parameterising a feature file is to generate it from a template at compile-time. Then at runtime your cucumber runner executes the generated feature file.
This is fairly easy to do if you are using gradle. Here is an example:
In build.gradle, add groovy code like this:
import groovy.text.GStringTemplateEngine
task generateFeatureFiles {
doFirst {
File featuresDir = new File(sourceSets.main.output.resourcesDir, "features")
File templateFile = new File(featuresDir, "myFeature.template")
def(String bestDay, String currentDay) = ["Friday", "Sunday"]
File featureFile = new File(featuresDir, "${bestDay}-${currentDay}.feature")
Map bindings = [bestDay: bestDay, currentDay: currentDay]
String featureText = new GStringTemplateEngine().createTemplate(templateFile).make(bindings)
featureFile.text = featureText
}
}
processResources.finalizedBy(generateFeatureFiles)
myFeature.template is in the src/main/resources/features directory and might look like this:
Feature: Is it $bestDay yet?
Everybody wants to know when it's $bestDay
Scenario: $currentDay isn't $bestDay
Given today is $currentDay
When I ask whether it's $bestDay yet
Then I should be told "Nope"
Running the build task will create a Friday-Sunday.feature file in build/src/main/resources with the bestDay and currentDay parameters filled in.
The generateFeatureFiles custom task runs immediately after the processResources task. The generated feature file can then be executed by the cucumber runner.
You could generate any number of feature files from the feature template file. The code could read in parameters from a config file in your resources directory for example.
I have a cookbook "blah-deploy-nodejs-from-git" cookbook that installs a nodejs codebase from GIT and calls NPM install on the directory. It has the following attributes
git_repo
branch
destination
I have then written cookbooks that wrap that cookook for inidividual sites, that need to get installed. In this particar case "blah-pricing" and "blah-notifications" which have different overriding attributes:
me#me cat cookbooks/blah-svc-pricing/attributes/default.rb
node.override[:blah_deploy_nodejs_from_git][:destination] = "/var/blah/pricing"
node.override[:blah_deploy_nodejs_from_git][:branch] = "master"
node.override[:blah_deploy_nodejs_from_git][:git_repo] = "https://hqdevgit01.blah.lan/micro-services/blah-pricing.git"
me#me:~/chef-repo$ cat cookbooks/blah-svc-notifications/attributes/default.rb
node.override[:blah_deploy_nodejs_from_git][:destination] = "/var/blah/notifications"
node.override[:blah_deploy_nodejs_from_git][:branch] = "master"
node.override[:blah_deploy_nodejs_from_git][:git_repo] = "https://hqdevgit01.blah.lan/micro-services/blah-notifications.git"
And then the recipe is the same in both cases:
include_recipe 'blah-deploy-nodejs-from-git'
Unfortunately it is applying the inner recipe only once even though my node has both cookbooks applied to it. My understanding was that wrapper cookbooks are used to customize a cookbook and make it unique.
Can encapsulate the inner cookbook to two different cookbooks, with different attributes, and have the wrapper cookbooks both apply that inner recipe? OR Am I going to have to completely replicate the code that is in the inner cookbook?
This is due to a basic misunderstanding of how chef works. Recipes are not meant to be a procedure for how to do something, they are meant to be a declaration of what that something should look like. As such, you need to think of them as describing the end state, not the process for getting there.
Thus, chef will never run a recipe twice. And attributes really should not be changed mid run (unless they are updated to indicate something that happens mid run. Luckily, there are other chef capabilities that can solve your problem. You need either a definition or an LWRP (light weight resource provider)
Definitions are just groups of resources that are often repeated. So you can create a definition and then later call it multiple times in the same recipe with different attributes. Much like what you currently are doing with your recipe.
While definitions are sometimes appropriate, LWRPs are generally more powerful, and have become the prefered approach for most repetitive (library like) task in Chef. With LWRPs you will define a new chef primitive (much like file, service, etc), and then write the code for accomplishing the goal of that primitive. You can then use these resources anywhere in your recipes. In your case, you'd have an npm_deployer resource that took attributes for repo, branch, and destination. It would then do the work that is currently in your deployer recipe. Your "wrapper" recipes would then stop calling include_recipe and instead just declare a npm_deploy resource and pass in the attributes needed.
I have some common methods used in a couple different specs, I want to extract them to some place like a spec helper that is accessible from all specs. Anyone know how to do this?
Here is something that sorta quacks like a spec_helper.
# _spec_helper.rb
module SpecHelper
::App::Persistence = {}
# global `before :each` ish
def self.extended(base)
base.before do
::App::Persistence.clear
end
end
def foo_helper
end
end
And then use it:
# my_view_spec.rb
describe "MyView" do
extend SpecHelper
before do
foo_helper
end
...
Two things to bear in mind:
Spec helper file is named in such way that it gets loaded first (leading underscore)
When running individual specs (e.g. files=my_view_spec.rb) helper file must go along - files=spec/my_view_spec.rb,spec/_spec_helper.rb
I just throw my common methods used in specs as they are (not encapsulated in a Module or anything) in a spec/support/utilities.rb file and Rubymotion seems to pick them up fine, though I don't know if this is the "proper" way to do this.
According to current http://www.rubymotion.com/developer-center/articles/testing/#_spec_helpers
Spec helpers are created under the spec/helpers directory of a RubyMotion project. An example could be spec/helpers/extension.rb.