This time I’m keeping it ultra-simple: The Stack.
I’m sure we all know what a stack is, but to briefly remind
you it’s a LIFO (Last-in, first-out) container. In a classic stack elements are
added on top of the stack using the “Push” command and removed from the top
one-by-one using “Pop”. When an element is popped it is removed from the stack
and returned. It is not possible to reference elements that are not on the top
of the stack.
A not-so-classic stack variation contains a “Peek” function
that will return the top element of the stack without removing it.
So without further ado let’s jump into modeling!
Implementation
For the purpose of this post I’ve re-implemented the wheel
and written a fixed-size (10) integer based stack:
public class
Stack
{int[] items = new int[10];
int count = 0;
public Stack()
{
}
public void Push(int x)
{
if (count >= items.Length)
throw new Exception("Stack overflow");
for (int i = count; i > 1; i--)
items[i] = items[i-1];
items[count++] = x;
}
public int Peek()
{
return items[0];
}
public int Pop()
{
if (count <= 0)
throw new Exception("Popping empty stack");
int x = items[0];
for(int i = 0; i < count; i++)
items[i] = items[i+1];
count--;
return x;
}
}
Pretty standard stuff.
Model
For modeling the stack we define the three actions and model
them like this:
class StackModel
{SequenceContainer<int> inputSequence = new SequenceContainer<int>();
[Rule(Action = "new this.Stack()")]
StackModel()
{
}
[Rule(Action = "this.Push(x)")]
void PerformPush(int x)
{
Condition.IsTrue(inputSequence.Count < 4);
inputSequence.Add(x);
}
[Rule(Action = "this.Peek()/result")]
int PerformPeek()
{
Condition.IsTrue(inputSequence.Count > 0);
return inputSequence[inputSequence.Count - 1];
}
[Rule(Action = "this.Pop()/result")]
int PerformPop()
{
Condition.IsTrue(inputSequence.Count > 0);
int x = inputSequence[inputSequence.Count - 1];
inputSequence.RemoveAt(inputSequence.Count - 1);
return x;
}
}
In the config.cord script I have limited the input values for the “Push” action to 0 and 1. Also the model is limited to having at most 3 elements in the stack at any given time.
Exploration of this model yields:
Test cases
There’s an interesting fact about our model. Because every
state is connected to all other states, traversing the graph with the “Long tests”
strategy will yield only one test case. I don’t like this, so I’m going to
change it to use “Short tests” strategy instead.
The generated test cases are:
An interesting fact is that I found two bugs in my
implementation of the stack when running these generated tests (that tells you a
bit about my coding skills – it’s a good thing I’m hired as a tester...):
1.
Off by one error in the loop that shifts elements
out
2.
Push was inserting new elements at the bottom of
the stack
for
(int i = count; i > 1; i--)items[i] = items[i-1];
items[count++] = x;
I left the bugs in the sample, which you by the way can find in the reference list [1].
Testing .NET class
libraries
Okay, so now we have tested that our stack implementation is
not working, let’s try something funny! So .NET contains an implementation of a
stack in System.Collections.Stack, wouldn’t it be interesting to check that the
guys in .NET did a better job than me implementing a stack? Actually, SpecExplorer
allows us to test any .NET class directly. We simply change the using
declaration in the config.cord file:
using
System.Collections;
//using StackModel.Sample;
And the type binding in the StackModel.cs file:
[TypeBinding("System.Collections.Stack")]
//[TypeBinding("StackModel.Sample.Stack")]
And re-generate our test suite. The model obviously stays the same (we didn’t change that) but the tests will now call into the .NET stack class implementation.
But wait a minute? We implemented an integer based stack,
and the .NET stack holds any objects, then why does this work? It turns out
that because the interface for our stack is a subset of that of the more
generic .NET stack class, SpecExplorer can automatically cast the arguments
from integers to Objects, and everything works out-of-the-box! That is pretty
sweet.
Big surprise, running the generated test cases, they all
pass! Kudos to the .NET guys for a good job, and I think I will stay on the
testing path for now J
And finally I would really appreciate some feedback, so if you made it all the way down here, please leave a comment telling me what you think was good/bad about this post - thanks!
References
[1] Stack
Model
Hi,
ReplyDeleteI liked it a lot. It will be good if there is a discussion on multiple stacks. I think it becomes tricky to create two stacks, for example, inter leave them using |||. The reason is that the universal operator (_) may not make sure all instances of stacks are used during exploration. Do you have any comments or thoughts on multiple instance of stacks and interleave them to invoke methods of the Stack class?
I think you have to be careful when to choose instance based modeling over static models (like this stack model for example is static). In this example where I'm testing a stack in isolation, you don't really benefit from doing instance based modeling because the individual instaces wouldn't interact.
DeleteWhere it makes sense to do instance based modeling, is if you are modeling a _system_ that comprises multiple stacks. Here the focus shifts from testing the stacks in isolation, to testing the system of inter-dependent stacks. One example where you might have multiple stacks. You could think of a system with multiple bins defining a work-flow, these bins could be represented by stacks, items would then move from one bin to another. Some interesting invariants that a model could check would be conservation of items, meaning that the sum of number of items should be constant, as items cannot disappear in this system. Then the model would consist of all actions/rules that are implemented to move items around... anyway, the point being now we are testing the actual system and no longer the stacks in isolation, and we would thus define system invariants for purpose of validation.