Episode #331
This episode uses Swift 4, Xcode 9.2.
- So far we've been
talking about really basic
demo worthy tests that are just asserting
false or true just so we
can see that the mechanics
of test running.
But we want to talk a little
bit about the realistic
examples that we are going
to see and the varying levels
of complexity that our test can encounter.
And so I'm want to start
off with a little bit
of a better example.
Still going to be basic
enough where we can
poke around with it
So I'm going to create a
class called Calculator.
And our calculator's
going to have a method
on it called Add.
And it's going to take
two numbers an X and a Y
and add them together.
And for the sake of the example
I'm just going to return
negative one here.
So for my test case class I
actually want this to be called
CalculatorTests.
And in the most general
case I would prefer to have
every class that I want
to write unit tests for
I'm going to have an
equivalent test case for that.
So we're going to have a calculator test
to test our calculator, we might
have a view controller test
case to test our view
controllers, we might have
something else that's going to test
that particular instance.
Now as things get more
complicated you may want to create
a test case per context
that something is done in.
And that's just going
be up to you basically
in a refactoring step to
break down large test suites
into smaller ones but I
would recommend starting out
with one test case class per
object that you want to test.
Okay so we want to create an
instance of our calculator
and so we might be tempted
to say something like
let calculator equals
calculator like this.
This is problematic because
our test case remember
is going to be created one
time and then all of our tests
are going to be used
on that same instance.
So if we interact with a calculator here
we might inadvertently
be changing the state
of that calculator in this test,
so we don't want to do that.
So instead we want to
declare our types with var
and use the implicitly unwrapped optional.
And instead we're going to
use the set up method here
to set up a new instance of our
calculator every single time
through our test run.
Like that so this is a
good habit to get into.
Don't reuse anything between tests.
Okay so we've got our
calculator here I want to
make a descriptive method
test adding two numbers.
And here I want to take our
calculator, I want to add
two numbers together so I'm
going to say let the result be
calculator.add and let's
say we add four and eight.
And then I've got my expected
value which I believe
is going to be 12.
And so now I can run my
assertion XETAssertEqual
my expression one is going
to be the result that we got
remember the results or the actual value.
And then the expected value second.
So now that we have that.
We should see that this
test is actually failing.
Actually I changed the name of
this test so I need to change
the way its run down here.
We should see this test
fail because negative one
is not equal to 12.
So adding is not working
out correctly here.
And we can fix this test by changing this
to return X plus Y.
And now our tests are passing.
So this is the quick feedback that I like
about using Playgrounds to run tests
because I didn't have to type,
I didn't have to do anything
to make the test run I just
typed the code and it ran it.
Okay, so this is what I like
to call value based testing
we give it some static inputs
and we get back a result.
This is the easiest possible
type of test we could write.
This is called value based
testing because we're only
testing with values.
Our calculator doesn't have
to reach out to the internet
to pull down stock quotes or anything.
We don't need to save
anything to a data base.
There's no side effects that happens here.
It's a pure function, it's
inputs and it's outputs.
And these things are desirable.
If you can design your
software entirely with inputs
and outputs that have no side effects
it becomes really, really easy to test.
Unfortunately the real
world isn't quite so easy
to work with and so we're
going to end up with tests
that are more complicated than this.
But I think that as we're
building our applications
it's worth striving for tests
that or application code
that is really geared
towards inputs and outputs.
Try to build code that
doesn't have side effects.
If we have an add method
that writes to disk
this is probably a bad
responsibility for this method.
So in this way the desire to
have an easily testable system
can also help in designing
our software in a way
that is testable which
happens to be inherently good
object oriented design anyway.
So that's basically what
we want to strive for
is value based testing.