rspec build many tests at once with each, problem with memoized variables - unit-testing

I need to build some quite repetitive rspec tests, and would like to DRY it up a bit.
describe 'filter' do
let!(:filter_1) { create :filterable, value: 1 }
let!(:filter_2) { create :filterable, value: 2 }
let!(:filter_3) { create :filterable, value: 3 }
let!(:filter_4) { create :filterable, value: 4 }
expected = {
a: filter_1,
b: filter_2,
c: filter_3,
d: filter_4
}
expected.each do |filter, expected|
describe "by #{filter}" do
let(:expected) { [ expected ] }
it "shows only #{filter}" do
get :index, filter: filter
expect(assigns[:filters]).to match_array expected
end
end
end
end
I'm basically defining a hash with my filters and expected results, and build a test for each one.
the problem is how to specify the expected result in the hash.
this example raises an error:
rspec-core-3.4.4/lib/rspec/core/example_group.rb:667:in `method_missing': `filter_1`
is not available on an example group (e.g. a `describe` or `context` block).
It is only available from within individual examples (e.g. `it` blocks)
or from constructs that run in the scope of an example (e.g. `before`, `let`, etc).
(RSpec::Core::ExampleGroup::WrongScopeError)```
I think the memoized variables are only available within an `it` block.
how could I solve this?
thanks,

describe 'filter' do
expected_filters = -> () do
filter_1 = '1'
filter_2 = '2'
{ a: filter_1, b: filter_2 }
end
expected_filters.().each do |filter, expected|
describe "by #{filter}" do
it "shows only #{filter}" do
expect(filter).to be_instance_of(Symbol)
expect(expected).to be_instance_of(String)
end
end
end
end
But if you need the code in Let! to be run for any example try to find a solution with a method definition but you have to change something in the test code as actually is.

Related

Incremental regex matching in Kotlin

This code snipped searches for the first regex match in a file for which one of the capture groups matches a local variable, and then obtains the value of the other capture group.
Is it possible to write this in an idiomatic, more efficient version which doesn't find all matches up front, but rather matches incrementally, without an explicit loop?
val id = ANCHOR_REGEX.findAll(apiFile.readText())
.find { label == it.groups["label"]?.value }
?.let { it.groups["id"]?.value }
Yes, there is. And you've already done it. findAll returns a Sequence<MatchResult>, and just to be extra sure, we can look in the source code and see the implementation ourselves.
public actual fun findAll(input: CharSequence, startIndex: Int = 0): Sequence<MatchResult> {
if (startIndex < 0 || startIndex > input.length) {
throw IndexOutOfBoundsException("Start index out of bounds: $startIndex, input length: ${input.length}")
}
return generateSequence({ find(input, startIndex) }, MatchResult::next)
}
That's generateSequence from the standard library, which produces lazy iterators whose next element is determined by calling the function repeatedly. find on iterables is also perfectly capable of short-circuiting, so the code you've already written will incrementally find matches until it finds the one you want or exhausts the string.

Linq get element from string list and a position of a char in this list

i want to get an element from a list of string and get the position of a char in this list by using linq ?
Example :
List<string> lines = new List<string> { "TOTO=1", "TATA=2", "TUTU=3"}
I want to extract the value 1 from TOTO in the list
here is the begin of my code
var value= lines.ToList().Single(x =>x.Contains("TOTO=")).ToString().Trim();
How to continue this code to extract 1 ?
Add this :
value = value[(value.LastIndexOf('=') + 1)..];
Using LINQ you can do this:
List<string> lines = new List<string> { "TOTO=1", "TATA=2", "TUTU=3" };
int value = lines
.Select(line => line.Split('='))
.Where(parts => parts[0] == "TOTO")
.Select(parts => int.Parse(parts[1]))
.Single();
If you always expect each item in that list to be in the proper format then this should work, otherwise you'd need to add some validation.
Similar to What #jtate proposed, Some minor enhancements can help.
int value = lines
.Select(line => line.Split(new []{ '=' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => string.Equals(parts[0], "TOTO", StringComparison.InvariantCultureIgnoreCase))
.Select(parts => int.Parse(parts[1]))
.SingleOrDefault();
SingleOrDefault - If you don't find any elements matching your constraints, Single() would thow an exception. Here, SingleOrDefault would return 0;
String.Equals - would take care of any upper lowere or any culture related problems.
StringSplitOptions.RemoveEmptyEntries - would limit some unecessary iterations and improve performance.
Also see if you need int.TryParse instead of int.Prase. All these checks would help cover edges cases in production

Casting regex match to String

I created a simple code in Scala that checks whether an input is correctly formatted as HH:mm. I expect the code to result in an Array of valid strings. However, what I'm getting as a result is of type Any = Array(), which is problematic as when I try to print that result I get something like that:
[Ljava.lang.Object;#32a59591.
I guess it's a simple problem but being a Scala newbie I didn't manage to solve it even after a good few hours of googling and trial & error.
val scheduleHours = if (inputScheduleHours == "") {
dbutils.notebook.exit(s"ERROR: Missing param value for schedule hours.")
}
else {
val timePattern = """^((?:[0-30]?[0-9]|2[0-3]):[0-5][0-9])$""".r
val inputScheduleHoursParsed = inputScheduleHours.split(";").map(_.trim)
for (e <- inputScheduleHoursParsed) yield e match {
case timePattern(e) => e.toString
case _ => dbutils.notebook.exit(s"ERROR: Wrong param value for schedule hours: '${inputScheduleHours}'")
}
}
The problem is that some branches return the result you want and others return dbutils.notebook.exit which (I think) returns Unit. Scala must pick a type for the result that is compatible with both Unit and Array[String], and Any is the only one that fits.
One solution is to add a compatible value after the calls to dbutils.notebook.exit, e.g.
val scheduleHours = if (inputScheduleHours == "") {
dbutils.notebook.exit(s"ERROR: Missing param value for schedule hours.")
Array.empty[String]
}
Then all the branches return Array[String] so that will be the type of the result.

Nicer way to access match results?

My requirement is to transform some textual message ids. Input is
a.messageid=X0001E
b.messageid=Y0001E
The task is to turn that into
a.messageid=Z00001E
b.messageid=Z00002E
In other words: fetch the first part each line (like: a.), and append a slightly different id.
My current solution:
val matcherForIds = Regex("(.*)\\.messageid=(X|Y)\\d{4,6}E")
var idCounter = 5
fun transformIds(line: String): String {
val result = matcherForIds.matchEntire(line) ?: return line
return "${result.groupValues.get(1)}.messageid=Z%05dE".format(messageCounter++)
}
This works, but find the way how I get to first match "${result.groupValues.get(1)} to be not very elegant.
Is there a nicer to read/more concise way to access that first match?
You may get the result without a separate function:
val line = s.replace("""^(.*\.messageid=)[XY]\d{4,6}E$""".toRegex()) {
"${it.groupValues[1]}Z%05dE".format(messageCounter++)
}
However, as you need to format the messageCounter into the result, you cannot just use a string replacement pattern and you cannot get rid of ${it.groupValues[1]}.
Also, note:
You may get rid of double backslashes by means of the triple-quoted string literal
There is no need adding .messageid= to the replacement if you capture that part into Group 1 (see (.*\.messageid=))
There is no need capturing X or Y since you are not using them later, thus, (X|Y) can be replaced with a more efficient character class [XY].
The ^ and $ make sure the pattern should match the entire string, else, there will be no match and the string will be returned as is, without any modification.
See the Kotlin demo online.
Maybe not really what you are looking for, but maybe it is. What if you first ensure (filter) the lines of interest and just replace what needs to be replaced instead, e.g. use the following transformation function:
val matcherForIds = Regex("(.*)\\.messageid=(X|Y)\\d{4,6}E")
val idRegex = Regex("[XY]\\d{4,6}E")
var idCounter = 5
fun transformIds(line: String) = idRegex.replace(line) {
"Z%05dE".format(idCounter++)
}
with the following filter:
"a.messageid=X0001E\nb.messageid=Y0001E"
.lineSequence()
.filter(matcherForIds::matches)
.map(::transformIds)
.forEach(::println)
In case there are also other strings that are relevant which you want to keep then the following is also possible but not as nice as the solution at the end:
"a.messageid=X0001E\nnot interested line, but required in the output!\nb.messageid=Y0001E"
.lineSequence()
.map {
when {
matcherForIds.matches(it) -> transformIds(it)
else -> it
}
}
.forEach(::println)
Alternatively (now just copying Wiktors regex, as it already contains all we need (complete match from begin of line ^ upto end of line $, etc.)):
val matcherForIds = Regex("""^(.*\.messageid=)[XY]\d{4,6}E$""")
fun transformIds(line: String) = matcherForIds.replace(line) {
"${it.groupValues[1]}Z%05dE".format(idCounter++)
}
This way you ensure that lines that completely match the desired input are replaced and the others are kept but not replaced.

Swift substring initialization

Currently I am trying to mess with this in playground before I implement some version of this into my actual code. I am trying to take a string and print out 4 characters. The code that is shown below, I am planning on using in a loop and increment the starting and ending position by 4 which is why there are variables currently at the starting and ending points. Before I can even get there however, I am getting an error:
error: cannot invoke initializer for type 'Range' with an argument list of type '(start: String.CharacterView.Index, end: String.CharacterView.Index)'
var str_start = 0
var str_end = 4
let sub_str = initial_str.substring(Range<String.Index>(start: initial_str.startIndex.advancedBy(str_start), end: initial_str.endIndex.advancedBy(str_end)))
I've already looked at these sources but to no avail:
Creating Range<String.Index> from constant Ints
Cannot invoke initializer for type 'Range<String.Index>' with an argument list of type '(start: String.Index, end: String.Index)'
Any assistance is greatly appreciated, and I apologize if it is a simple fix.
Here's one way to do it:
let initialString = "foo bar"
let newStartIndex = initialString.index(initialString.startIndex, offsetBy: 1)
let newEndIndex = initialString.index(initialString.endIndex, offsetBy: -1)
let substring = initialString[newStartIndex..<newEndIndex]
// this also works, but it needs `import Foundation`:
// let substring = initialString.substring(with: newStartIndex..<newEndIndex)
print(substring)
Output:
oo ba