Say I have the sample data driven spec:
class DataDriven extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
[a,b,c] << runSomeSQL()
}
}
Is there a way to tell Spock to officially ignore (as a contrived example) where a < 7? EG as if I didn't use the data driven mechanism and simply #Ignore-ed the a < 7 cases
The only solution is to comment out the lines to be ignored.
Related
Let's say there is a function to determine if a button should be visible.
fun isButtonVisible(fitlers: List<Filters>, results: List<Shop>, isLoading: Boolean) {
return fitlers.isNotEmpty() && results.isEmpty() && !isLoading
}
Now I would like to test this function using PBT like:
"the button should be visible if filters is not empty and results is empty and is not loading" {
forAll { filters: List<Filters>, results: List<Shop>, isLoading: Boolean ->
val actual = isButtonVisible(filters, results, isLoading)
// Here reimplement the logic
val expected = filters.isNotEmpty() && results.isEmpty() && !isLoading
assertThat(actual).isEqual(expected)
}
}
It seems that I just reimplement the logic again in my test, is this correct? If not, how can I come up with another properties if the logic is just simple combinations of several flags?
that is not right.
you should not have to calculate what the expected value should be during the test, you should know what the result should be, set it as such and compare it against the actual result.
Tests work by calling the method you want to test and comparing the result against an already known, expected value.
"the button should be visible when filters are not empty, results is empty, isLoading is false " {
forAll { filters: List<Filters>, results: List<Shop>, isLoading: Boolean ->
val actualVisibleFlag = isButtonVisible(filters, results, isLoading)
val expectedVisibleFlag = true
assertThat(actualVisibleFlag ).isEqual(expectedVisibleFlag )
}
}
Your expected value is known, this is the point I am trying to make.
For each combination of inputs, you create a new test.
The idea here is that when you have a bug you can easily see which existing test fails or you can add a new one which highlights the bug.
If you call a method, to give you the result of what you think you should get, well, how do you know that method is correct anyway? How do you know it works correctly for every combination?
You might get away with less tests if you reduce your number of flags maybe, do you really need 4 of them?
Now, each language / framework has ( or should have ) support for a matrix kind of thing so you can easily write the values of every combination
Another question is if there is any better way to write this method?
Public decimal CalculateTotalPrice(List<product> items)
{
decimal totalPrice = 0.m;
foreach(Product p in items)
{
if(p.Offer == "")
calc = new DefaultCalc();
else if(p.Offer == "BuyOneGetOneFree")
calc = new BuyOneGetOneFreeCalc();
else if(p.Offer == "ThreeInPriceOfTwo")
calc = new ThreeInPriceOfTwoCalc()
totalPrice += calc.Calculate(p.Quantity, p.UnitPrice);
}
return totalPrice;
}
You should probably review Polly Want a Message, by Sandi Metz
How to unit test a method that is having multiple object creation in switch statement?
An important thing to notice here is that the switch statement is an implementation detail. From the point of view of the caller, this thing is just a function
Public decimal CalculateTotalPrice(List<product> items);
If the pricing computations are fixed, you can just use the usual example based tests:
assertEquals(expectedPrice, CalculateTotalPrice(items));
But if they aren't fixed, you can still do testing based on the properties of the method. Scott Wlaschin has a really good introduction to property based testing. Based on the logic you show here, there are some things we can promise about prices, without knowing anything about the strategies in use
the price should always be greater than zero.
the price of a list of items is the same as the sum of the prices of the individual items.
if there is any better way to write this method?
You might separate choosing the pricing strategy from using the strategy. As Sandi notes, that sort of construct often shows up in more than once place.
foreach(Product p in items)
{
calc = pricing(p.Offer);
totalPrice += calc.Calculate(p.Quantity, p.UnitPrice);
}
"pricing" would then become something that you pass into this function (either as an argument, or as a dependency).
In effect, you would end up with three different kinds of test.
Checks that pricing returns the right pricing strategy for each offer.
Checks that each strategy performs its own calculation correctly.
Checks that CalculateTotalPrice compute the sum correctly.
Personally, I prefer to treat the test subject as a single large black box, but there are good counter arguments. Horses for courses.
Constructors can not be mocked (at least with free mocking frameworks).
Write tests without mocking as far as your tests run fast and test case setup is not very very complicated.
In your particular case you should be able to write tests without mocking.
Prepare data
var products = new List<Product>
{
new Product { Quantity = 10, UnitPrice = 5.0m, Offer = "" },
new Product { Quantity = 2, UnitPrice = 3.0m , Offer = "BuyOneGetOneFree" },
new Product { Quantity = 3, UnitPrice = 2.0m , Offer = "ThreeInPriceOfTwo" },
}
// prepare expected total
var expected = 57.0m; // 10 * 50.0 + 1 * 3.0 + 2 * 2.0
// Run the test
var actual = CalculateTotalPrice(products);
actual.Should().Be(expected); // pass Ok.
With this approach tests will not depend on implementation details.
You will be able to freely play with designs without rewriting tests every time you change your implementation logic.
The other answers are technically fine, but I would suggest one thing:
if(p.Offer == "")
calc = new DefaultCalc();
else if(p.Offer == "BuyOneGetOneFree")
calc = new BuyOneGetOneFreeCalc();
else if(p.Offer == "ThreeInPriceOfTwo")
calc = new ThreeInPriceOfTwoCalc()
should absolutely go into its own method/scope/whatever.
You are mapping a string to a specific calculator. That should happen in one place, and one place only. You see, first you do that here. Then some method method comes around that needs the same mapping. So you start duplicating.
I have to iterate over some map and for every key, to make checks based on the current element,so I tried:
class TestSuite extends Specification {
#Shared
def elements
def setupSpec{
elements = ['a.txt':1,'b.txt':2]
}
#Unroll
def 'test for #first and #second'() {
expect:
true
where:
[first, second] << [elements.keySet(), elements[first].findResults { key, value->
key.substring(key.indexOf('.') + 1)
}].combinations()
}
}
but Spock fails and says that first is unknown.
How can I do that so that the two values to be in the name of the test so in unroll to see their values?
Edited
I have to say that your code does not make much sense. I have hesistated to just downvote your question instead of replying. There is so much wrong with this question. Just shows a lack of effort in formulating your question.
The first red flag is that you did not even try to compile this code. You have a map with String keys and integer values:
your map litteral does not compile. key/value pairs are separated by commas in groovy.
You call methods of this map that (Map#key() ?) are not in the API.
Then, there are two possibilities, either your imaginary key() method returns the key, which does not make any sense, because first IS the key already, or key() returns the value, which is really bad naming. But bad naming is not all, because then you call toUpperCase() on an integer.... This is a mess!
Nevertheless I am going to show you how you can base the value of a where variable on the value of another where variable, because that's the core part of your question, with
import spock.lang.*
class MyFirstSpec extends Specification {
//elements needs to be Shared to be used in a where block
#Shared
def elements = ['a':1,'b':2]
#Unroll
def 'test for #first and #second and #third'() {
expect:
true
where:
first << elements.keySet()
and:
second = elements[first]
third = first.toUpperCase()
}
}
resulting in
- test for a and 1 and A
- test for b and 2 and B
How do I write a simple ABAP Unit Assert statement to check if any call, expression or other statement evaluates to true?
I can't see any basic assert() or assert_true() methods in CL_AUNIT_ASSERT while I'd expect those to be very common. I can approximate such an assert as follows, but is there no cleaner way?
cl_aunit_assert=>assert_equals(
act = boolc( lv_value > 100 OR lv_value < 2 )
exp = abap_true ).
cl_aunit_assert=>assert_equals(
act = mo_model->is_active )
exp = abap_true ).
Depending on your SAP NetWeaver stack you can (or should) use the updated ABAP Unit Class CL_ABAP_UNIT_ASSERT. This class is available at a Basis-Release >= 7.02. SAP declared this class as 'FINAL' so it´s impossible to inherit from it, but on the other side they added some ASSERT-Methods like the ASSERT_TRUE Method!
Here is a possible usage of this method:
cl_abap_unit_assert=>assert_true(
exporting
act = m_ref_foo->is_bar( l_some_var )
msg = 'is_bar Method fails with Input { l_some_var }'
).
For the releases I have access to, there's probably no shorter way than the one you outlined. You can create a subclass of CL_AUNIT_ASSERT and add your own static ASSERT_TRUE method. It's not a bad idea to do so and at the same time make your local ABAP Unit test class a subclass of that ZCL_AUNIT_ASSERT - this way, you can omit the cl_aunit_assert=> prefix which will save some keystrokes.
You cannot see such methods because there is no boolean type in ABAP.
While in Java, C++ or C, you are able to assign a result of a condition to a variable, like this
int i = 5;
boolean result = i > 3;
You cannot do the same thing in ABAP as there is no boolean type. Therefore what in other languages is a one liner, in ABAP it will always be more prolix.
DATA: i TYPE i VALUE 5.
DATA: result TYPE abap_bool.
IF i > 3.
result = abap_true.
ELSE.
result = abap_false.
ENDIF.
The thing you used seems to be a new feature, that has been recently added to the language and most of the customers will not be using for a long time. Also the CL_AUNIT_ASSERT class was created way before the new elements came to the language.
So right now, there is a possibility to write the above thing as one liner. However there is still no boolean type in the language.
DATA: i TYPE i VALUE 5.
DATA: result TYPE abap_bool.
result = boolc( i > 3 ).
On the other hand, there is no boolean type, but you could simply use ASSERT_INITIAL or ASSERT_NOT_INITIAL in this case, as boolean is emulated by either X (true) or space (false). The latter is an initial value in ABAP.
The cleanest way is to just fail:
if value > limit.
cl_abap_unit_assert=>fail( ).
endif.
Or a more informative:
cl_abap_unit=>fail( msg = 'Limit exceeded' ).
Is it possible to pass a value in where block like this.
I have tried this. But it fails and gives MissingPropertyException.
And I want the name1 and name2 to be inside the method.
def "length of names #name"() {
def name1 = "Spock"
def name2 = "Java"
expect:
name.size() == length
where:
name || length
name1 || 5
name2 || 2
}
Try this:
def "test length of names"() {
expect:
name.size() == length
where:
[name,length]<<getTestData()
}
def getTestData(){
[["Ram" ,3 ] ,["Test" ,4] ]
}
Hope that helps!!!
Thanks
Test data belongs in the where block, not hard coded in the test (feature) method.
One rough way to see this is to think of the test body (excluding the where block) as a method with some number of parameters -- 2 in your case, name and length. And then realize that the where clause just provides data values to the test runner to use when invoking your test method.
Spock uses Groovy magic to transform
def "test length of names"() {
expect:
name.size() == length
where:
name | length
"Spock" | 5
"Java" | 4
}
into something roughly like
def test_length_of_names(name, length) { // note the arguments
assert name.size() == length
}
and then tells the test runner to call the test once for each row in the where clause
test_length_of_names("Spock", 5)
test_length_of_names("Java", 4)
This approach
provides a nice separation of concerns between test logic and test data
allows relatively generic tests to be reused for a wide range of inputs and edge cases
avoids duplication in test code
supports a test design style that deserves the name data-driven testing.
This explanation leaves out a few details, such as creating an instance of the spec for each test, updating the name of each test invocation and calling the various setup and cleanup methods.
See the Spock documentation for more details.