STEPS architecture¶
-
class
stepler.base.BaseSteps(client)[source]¶ Base steps-class.
Steps-class aggregates steps over one resource. It means there are many inherited steps-classes for each resource.
Note
Technically there are three types of steps:
get-stepsTheir goal to retrieve information about resource and return it. If information is unavailable, a step should raise exception. Rules to code:
- step method name has prefix
get_, for ex:get_ips,get_projects,get_flavor,get_network. - step should return something in last code line, for ex:
return projects,return network. It makes easier code review and debugging. - step can include argument
check=True. It’s depends on context, for ex: step returns collection, but collection can be empty. And herecheck=Truewill help to check that collection has items.
- step method name has prefix
check-stepsTheir goal to validate input resource and raise exception if it’s invalid. Rules to code:
step method name has prefix
check_, for ex:check_image_presence,check_project_bind_status.step should return nothing.
step should use code to raise exception:
assert_thatfrom hamcrest,wait-methods which raiseTimeoutExpired,raisefor explicit exception, anothercheck_-steps.Warning
We don’t use python
assert, preferhamcrestlibrary.step should include argument
timeout=0to provide check during the time.
change-stepsTheir goal to change resource and check that changing is successful. Rules to code:
step method name starts with a verb denoting the action.
step has argument
check=True.step makes action over resource before check.
inside
if check:step should use code to raise exception:assert_thatfrom hamcrest,wait-methods which raiseTimeoutExpired,raisefor explicit exception, orcheck_-step.inside
if check:step should return nothing. Its section is for verification only.if step should return something (for ex:
return image), it should be afterif check:.if after resource changing need to update object with resource info, for ex:
server.get(), it must be insideif check:block (see explanation below)Warning
Explanation when in step it should to update object with resource information, for ex: when it should to call
server.get().Hint
Keep in mind, that objects like
server,image,volume,flavor, etc are not real resources. They are just objects with information about the state of resource at some point in time. And these info-objects should be updated from time to time.First of all its updating should NEVER call BEFORE resource changing. “Fresh” info-object must be passed to step.
Updating of info-object must be executed inside the same step, where resource is changed. But there two variants for ex:
# invalid variant def attach_floating_ip(self, server, floating_ip, check=True): self._client.add_floating_ip(server, floating_ip) server.get() if check: floating_ips = self.get_ips(server, 'floating').keys() assert floating_ip.ip in floating_ips
# valid variant def attach_floating_ip(self, server, floating_ip, check=True): self._client.add_floating_ip(server, floating_ip) if check: server.get() floating_ips = self.get_ips(server, 'floating').keys() assert floating_ip.ip in floating_ips
Let’s understand why second variant is better that first.
Change-step consist of two stages: - change resource state - retrieve resource state for checking.And
server.get()is resource state retrieving and must be a part of checking. This is more clearly for async steps. Let’s see how previous invalid variant would look as async:# invalid async variant def attach_floating_ip_async(self, server, floating_ip, check=True): self._client.add_floating_ip(server, floating_ip) server.get() if check: self.check_status(server, 'active') floating_ips = self.get_ips(server, 'floating').keys() assert floating_ip.ip in floating_ips
Here
server.get()doesn’t reflect actual state or resource, because resource is changed async. That whycheck_statusis used to wait some stable resource state, and it usesserver.get()inside itself. So we see double usage ofserver.get()and first its call absolutely useless.And in case, when step will be called with
check=False, callserver.get()doesn’t get actual resource state, because resource is changed async.It means that
server.get()is useful for checking only, but otherwise it’s useless.
Common rules:
- each step method has docstring, explaining its mission.
See also
Why do we make steps with optional verification
check=True?Sometimes (may be very rarely) we will need to make step without verification. In negative tests, for ex: try to create server without name and check that there is error. But more often we need positive verification by default to guarantee that step of a test was finished successfully and the test can go to another step.