I have read that with TDD we should approach the entity (function, class etc.) under test from the perspective of the user/caller of the entity. The gist being focusing on the public "interface". This in turn would drive the design and help reason about the design earlier.
But when we need to introduce mocks and stubs into our tests, isn't that an implementation detail?
Why would/should the "user" care about the other entities that are supposed to be there?
E.g.
How to start writing a test for the PlaceOrder service which should check with the credit card service if the user has enough money? Putting a mock for the credit card service whilst writing a test from the perspective of the PlaceOrder client looks out of place now - because it is an implementation detail; our PlaceOrder may call the credit card for each user or it can simply have a cache with scores provided at the creation time.
It's not clear-cut. As a catch-phrase says: Tests are specifications.
When you use Test Doubles you are, indeed, specifying how your System Under Test (SUT) ought to interact with its dependencies.
I agree that this is usually an implementation detail, but there will typically be a few dependencies of a more architectural character.
A common example is an email gateway. If your SUT should send email, that's an observable side effect that the user (or some other stakeholder) cares about.
While you can (and perhaps should?) also run full systems tests that verify that certain conditions produce real emails that land in certain real mailboxes, such test cases are difficult to automate.
Inserting a Test Double that can take the place of an email gateway and verify that the correct message was delivered to the gateway is not only an implementation detail, but an important part of the overall system. In such cases, using a Test Double makes sense.
Yes, Test Doubles specify behaviour, but sometimes, that's exactly what you want.
How much you should rely on this kind of design is an architectural choice. In addition to sending emails, you might choose to explicitly specify that a certain SUT ought to place a message on a durable queue.
You can create entire systems based on asynchronous messaging, which could imply that it'd be architecturally sound to let tests rely on Test Doubles.
In short, I find it a useful heuristic to use Test Doubles for architectural components, and rely mostly on testing pure functions for everything else.
For something like an order service, I wouldn't let the order service contact the payment gateway. Rather, I'd implement the order service operations as pure functions, and either pass in a payment token as function arguments, or let the output of functions trigger a payment action.
The book Domain Modeling Made Functional contains lots of good information about this kind of architecture.
On the other hand, the book Growing Object-Oriented Software, Guided by Tests contains many good examples of how to use Test Doubles to specify desired behaviour.
Perhaps you'll also find my article From interaction-based to state-based testing useful.
In summary: Tests are specifications. Test Doubles are specifications. Use them to specify the observable behaviour of the system. Try to avoid using them to specify implementation details.
But when we need to introduce mocks and stubs into our tests, isn't that an implementation detail?
Yes, in effect. A bit more precisely, it is additional coupling between your test and the details of your test subject's implementation.
There are two ideas in tension here. On the one hand, we want that our tests are as representative as possible of how our system will actually work; and on the other hand we each of our tests to be an controlled experiment of our implementation, without coupling to shared mutable state.
In some cases, we can disguise some of the coupling by using inert substitutes for our dependency as the default case, so that our implementation classes are isolated unless we specifically opt into a shared configuration.
So for PlaceOrder, it might look like using a default CreditCardService that always answers "yes, the customer has enough money". Of course, that design only allows you to test the "yes" branch in your code - to test a "no" branch, you are necessarily going to need to know how to configure PlaceOrder with a CreditCardService that declines credit.
For more on this idea, see the doctrine of useful objects.
More generally, in TDD we normally take complicated designs that are hard to test and refactor them into a design where something really simple but hard to test collaborates with something that is complicated but easy to test.
But for that to work at all, the components need to be able to talk to each other, and if you are going to simulate that communication in a test you are necessarily going to be coupled to the "implementation detail" that is the protocol between them.
For the case where that protocol is stable, having tests coupled to those details isn't, of itself, a problem in practice. There's coupling, sure, and cost of change, but if the probability of change is negligible then the expected cost of that coupling is effectively nothing.
So the trick is identifying when our tests would require coupling to an unstable implementation protocol, and figuring out how to best mitigate the risk of change.
Related
I am working on a use case diagram of a fully automated system. An external system will trigger just one use case of this system. Most of the other use cases are scheduled tasks and invoked by the timer. I have a use case that is invoked by the timer and it includes and extends two other use cases.
When I write the use case discriptions, who will be the actor for UC-2 and UC-3. Can a use case exists without an actor? I have seen lot of use case diagrams which has included or extended use cases without directly conneced to an actor. Please clarify this. Thanks in advance.
EDIT:
My system is connected with a DBMS. My system will analyse the database workload time to time and check whether any tuning can be done. That's all about my system. UC-1 is Analyse DBMS, UC-2 is Check Performance statistics and UC-3 is Tune the database. So timer is the one which invoke the use case. DBMS gets the benefit.Steps in Check Performance (UC-2) are repeated in another use case. That's why I put it as a separate use case. On the other hand Tune database(UC-3) will be performed only if there is a need for tuning after analyzing the database.
Officially this is correct. An included use case is a mandatory part of the including use case and an extending use case will optionally extend some use case. As #Ister notes in the comment, the actor for the included/extending use cases will be that of the main use case.
But, and this from my experience, you best avoid the use of those include/extend relations. In most cases, people tend to use them for functional decomposition which is plain wrong. A use case shall show an added value for its actor, not how a piece of functionality is used somewhere. In most cases a structuring of added value is not present and you can well show each bubble as a stand-alone use case or integrate it into the main use case. I recommend reading Bittner/Spence to get into matters.
Edit1: I just realize the sentence
trigger just one use case of this system
This rather sounds like you mix use cases with activities. It's not a piece of functionality. A use case is added value. There is a scenario (set) for a use case which has a trigger. But saying "a use case is triggered" sounds just wrong. You trigger the activities of a use case (where it starts getting technical). Most techies have difficulties making the cut and abstract to use cases. One more reason to read Bittner/Spence.
Edit2: In your comment you are talking about technical use cases. I admit that I had intensive discussions about this in the past. But you need to differentiate between technic and business. Your business use cases are Analyse DBMS, Check Performance, and Tune database. As such they are no UCs for a Timer but for some institution that cares about performance. The only UC for Timer is Trigger task (or something like that). There is a cut. The Timer does not care about business. It will happily trigger the shutdown of the system in the same way. It does not become a business actor only for that fact that it is technically used to start some business relevant process.
And not to forget: read Bittner/Spence. For me this book was an eye opener since I also had no idea about the intention of use cases.
It is likely that UC2 and UC3 are not really use cases but in fact steps/actions within the UC1. A good way to check to see whether you have real use cases is to ask your self if there is any actor (human, system or time etc.) that would have that use case as a complete goal. In other words would any actor initiate this use case. In addition to this - Occasionally you may have a use case that has no actor initiating. This should occur only in the case that there are multiple other use cases (i.e. at least 2)that will either include or extend that use case. In this case the use case is there for the purpose of facilitating reuse in your model and simplifying the model - in particular when you write your use case narrative. Don't go out of your way to create include and extend relationships always double check - if no actor is using the use case you are including or extending and no other use case is using it then you absolutely don't need it.
Use case is always a scenario executed by actor(s). In your situation primary actor is the system under discussion.
Technically you can introduce a timer as an actor who executes first UC-1 step, but better KISS. Just add a line before UC-1 steps via common convention:
Trigger: timer according to [Link to requirements about timer schedule].
If you have to write something beyond this line (for example timer checks conditions before triggering UC-1) then timer must become an actor.
Overall your use cases structure looks very valid for me, just don't forget to connect UC-1 to some higher goal anyhow. And please remove extends/includes as already mentioned.
JBehave is Java based & good for Java applications. JBehave also supports good HTML reporting. However, the problem with JBehave is that it only supports Story level and not Feature level.
Can any one here help me with a small explanation or documentation where I can understand how Cucumber supports Feature level?
A feature is an implementation of a capability (a capability might need more than one feature).
Let's look at Mike Cohn's description of a "story", since it's pretty good:
User stories are short, simple descriptions of a feature told from the
perspective of the person who desires the new capability, usually a
user or customer of the system. They typically follow a simple
template:
As a <type of user>, I want <some goal> so that <some reason>.
A good user story follows the INVEST principles, and this is where we start getting into scenarios:
Independent, which means it can be delivered on its own
A story may have one or more contexts in which the feature is going to work. The contexts are by their nature independent of other contexts.
Negotiable, so you can rewrite it
As you work through a story, you may find other contexts or outcomes that need to be considered. The capability which is the core of the feature is usually associated with the "when". For instance, if I wanted to be able to generate a report, the "when" would be, "When I generate the report..."
Valuable, so it delivers value to stakeholders
There may be several stakeholders with different outcomes. For instance, sending an email to say that a cab's been booked is important, but so is sending the booking confirmation to the driver! By considering different stakeholders, we come up with the outcomes for the scenarios that need to be considered.
Estimable, so you can estimate the size
If you can't estimate the size, try just getting one scenario working. This is the functional equivalent of Kent Beck's "spike". Incidentally the only reason you need an estimate is usually to work out whether it's worth doing, given other work that could be done, so treat this accordingly.
Small, which (quick edit as I realised I missed this letter) means you should have some level of certainty about it.
I actually prefer people knowing they have some level of uncertainty about it, and look to get feedback ASAP. If you're really certain about it (eg: login) then it's OK for it to be bigger, because you'll need feedback less frequently; you know what "working" looks like.
Testable, which means the related description must include enough to test it
Examples become tests as a nice by-product of this analysis.
Why do we do stories in the first place, though? Why not just deliver whole features?
It turns out that the work needed to get some features shipped ends up being pretty big, especially if you've got a lot of stakeholders involved, and we want to either slice them up so we can get value from them earlier, or we want to slice them so we can get feedback.
So a story might be a slice through a feature that can actually be shipped, or it might be something designed to get feedback.
Scenarios are a fantastic way of doing this! The feature can be narrowed down to the most valuable contexts, or different stakeholders whose outcomes need to be considered. Be careful not to eliminate stakeholders with transactional needs (the user at the ATM gets their money; the bank debits the account) or regulatory needs (the bank makes a major investment; the regulators see the change in capital reserves).
Scenarios aren't the only way of getting feedback on a feature. New UI? Hard-code it without any behaviour and show it to people. New report? Make up a mock copy. New feed that nobody's ever processed? Make a spike, see if you can get the information out of it that you think you can.
Otherwise, consider different contexts and stakeholders whose outcomes need to be considered, and consider different capabilities with their contexts and outcomes. Features implement the different requirements, whose behaviour is illustrated by the scenarios you've derived.
Since a story is a slice through a feature, and a feature implements a capability, this is a typical hierarchy:
Stakeholder
Goal
Capability
Feature
Story
Scenario
If you're trying to work out how to relate scenarios to stories and features, this isn't a bad way to go. You'll find it familiar if you look at Gojko Adzic's "Impact Mapping" or Matt Wynne's "Example Mapping" (I think we all got it from listening to Chris Matts).
Be careful because in reality this is a bit fuzzy; you'll make discoveries as you start to deliver, so don't break everything down ahead of time. I find capabilities make good planning-level artifacts, and are often associated pretty easily with "Epics". They also come with their own high-level tests: "Can our users do what they need to, for the contexts we need to consider, and the stakeholders whose outcomes are also needed?"
The trick with a story is to only consider what's needed to deliver value, until it's actually delivered... and then some of the rest will be the next thing that's needed, etc.
For more ideas, here's my blog on capability-based planning and lightweight analysis, and another one on splitting up stories.
For Cucumber, organize by capability and then (if you need to) by context, and tag your check-ins with the story number (most CI tools, electronic boards and version control systems support this). It's OK for a feature or story to create more scenarios.
How do I determine what should I add to my use case diagrams? 1 for each button/form? Should things like sort and search be included? Or are they under "list items" for example? Though, a list of items seems understood?
The Use Case diagram is intended to help define the high-level business tasks that are important, not a list of functions of the system. For example, a system for use in customer service might involve a research task of looking up information to help someone on a support call.
Most of the literature describes Use Cases as a starting point for defining what the system needs to accomplish. The temptation has always been to be as complete as possible; adding ever more details to define the use case down to a functional (code-wise) level. While it is useful to have a comprehensive understanding of the requirements, the Use Case diagram is not intended to provide that level of documentation.
One thing that makes the issue worse is the syntax which I've never seen used in a working project. It isn't that the terms aren't useful, it's due to the lack of consensus over when to use either term for a given use case. The UML artifacts expect a process that is more focused on the business language instead of the implementation language - and by that I do not mean a computer language. The tendency by some has been to approach the diagrams with a legalistic bent and worry about things like when to use for related use cases or how to express error-handling as exceptions to a defined list of process tasks.
If you have ever tried to work through the Automated Teller Machine (ATM) example, you'll know what I mean. In the solar system of UML learning, the ATM example is a black hole that will suck you into the details. Avoid using it to understand UML or the Object Oriented Analysis and Design. It has many of the problems, typical of real-world domains, that distract from getting an overall understanding even though it would make for a good advanced study.
Yes, code will eventually be produced from the UML artifacts, but that does not mean they have to be debated like a treaty in the Senate.
The OMG UML spec says:
Use cases are a means for specifying required usages of a system. Typically, they are used to capture the requirements of a system, that is, what a system is supposed to do. The key concepts associated with use cases are actors, use cases, and the subject. The subject is the system under consideration to which the use cases apply. The users and any other systems
that may interact with the subject are represented as actors. Actors always model entities that are outside the system.
The required behavior of the subject is specified by one or more use cases, which are defined according to the needs of actors. Strictly speaking, the term “use case” refers to a use case type. An instance of a use case refers to an occurrence of the
emergent behavior that conforms to the corresponding use case type. Such instances are often described by interaction specifications.
An actor specifies a role played by a user or any other system that interacts with the subject. (The term “role” is used
informally here and does not necessarily imply the technical definition of that term found elsewhere in this specification.)
Now most people would agree that business and user level interactions are the sweet spot, but there is no limitation. Think about the actors/roles being outside of the main system/systems you are focusing on. But in one view a system could be an actor, but in another the implementer of other use cases.
I am in the process of writing use cases for a website
I would like to know whether conditional flow can be incorporated in websites
Eg: Payment Success- Failure
Or should it be documented as Activity or state diagranm?
Use Case descriptions, as mentioned in another answer, should most importantly cover the successful use cases of the system. However, it is also important to mention some of the special cases, in which system and actor should interact differently. Be warned, it is important to identify special conditions related to the use of the system, not technical details and rather than describing the decision logic, you should describe the special conditions. Alistair Cockburn in Writing effective Use Cases uses extensions of use cases to describe those scenarios as branches (with pre- and post-conditions) from the main scenario, with possible merge. Other experts use alternative flows for the same use case description. If you are truly after capturing the algorithmic logic, your idea of considering activity or state diagrams is a better choice.
As mentioned, payment success would be your main path. Payment failure, as in the user's credit card was declined, is an acceptable outcome and should have an alternate path.
If the payment fails because of something like network connection issues, that would be a exception and handled accordingly. (I've always treated exception paths as a type of alternate path.)
DDD teaches us to build our classes like their real-world prototypes.
So instead of using setters
job = new Job
job.person = person
job.since = time.Now()
job.title = title
we define well-named methods in our aggregation root
job = person.promote(title, /** since=time.Now() **/)
Now the tricky part
Assume we have an UI for an HR where he/she enters a new title via the HTML form and makes a typo like "prgrammer" (Of course in real application there'd be a select list, but here we have a text input), or selects a wrong date (like default today)
Now we have a problem. There are no typos in real world. Our John Doe is definitely a "programmer" and never a "prgrammer"
How do we fix this typo in our domain model?
Our Person has just promote, demote, fire, etc. methods, which reflect the HR domain model.
We could cheat a little bit and change the Job record directly, but now we have a Job.setTitle method, that doesn't reflect our domain model and also, setters are evil, you know.
That may look a little "academic", but that really bugs me when I try to build a good domain model for a complex application
Another side of DDD is invariants - "always valid" entity. And when you try to break this invariant (some rule) you must stop execution and say "loudly" adout this (throw exception). So, you need to have a list of valid titles and when you try to change title (does not matter how) to invalid state, you must throw some usefull exception.
To "fix" typo situations you must separate operations in your domain promote is one operation (it may check something, sent contratulation email :) and so on). And edit operation - just to edit some properties. So, the differenece is in logic of operations. You can't call promote without some preconditions (for example, required experience of worker), but you can call edit and fix worker's name because of type.
And usually this operations are separated between different users: only HR's can promote but a worker can edit his name, if it's wrong.
This solution is very complicated for such example, but it's always with DDD.
The main concept - separate operations. Each one with their own conditions, permissions, rules.
A question about invariants (rules).
If a client is purely entering data, then the underlying domain in this (bounded) context is not very deep. In these cases, it's fine to use a CRUD style application and allow titles to be changed (setTitle()).
Just make sure dependent BCs (e.g., billing, vacation planning, ...) where no such thing as "invalid data" exists, can react to changes in your CRUD context appropriately.
The application should enforce input correctness before it reaches the domain layer, no garbage input. If that means using a dropdown for the job titles then so be it. You can validate the title against existing titles.
In my company of 18 thousand employees, typo happens all the time. You are going to have to be pragmatic about this and accept that there will be setters in your code (one way or another)
Pragmatic thinking is very much at the core of the domain driven design, and is what keep things simple.
"Purity is good in theory, but in practice it can be very difficult to achieve, and sometimes you must choose the pragmatic approach" - Patterns, Principles, and Practices of Domain-Driven Design (2015)
"There are no typos in real world", I get what you mean, but that's not true, there are human mistakes in real world scenarios and they should be accounted for in your domain if they are frequent.
If data entry errors aren't frequent it may not be worth the extra modeling efforts and those could perhaps just get fixed directly in the DB. It also depends if the business wishes to learn something about those mistakes or not.
However, if data entry errors are frequent, it might be an indicator that the system is perhaps not offering enough guidance and the business may wish to learn more about those errors in order to make processes more efficient and less error-prone.
You may wish to implement an operation such as job.correctTitle(...), perhaps in a BC dedicated to data corrections? Also, it's probably very rare that each and every piece of information will be erroneous so corrective operations can be segregated. That means you probably do not need a job.correctAllInformation(...) kind of operation.
This whole scenario is very fictive since job titles would usually be managed in a separate BC from where they are used and they would probably get picked from a list, therefore typos would be less frequent, but you will always have to deal with data entry errors. Choosing the appropriate solution is not always easy and will vary from case to case, but try to stay pragmatic and not strive for the perfect model in every sphere of your domain.