I'm new to BDD and I'm trying to figure out how I could write a test for pressing the back button in my app, or whether BDD requires me to write a test at all.
Here's a few example scenarios with functionality:
What: tapBackButton
Scenario: formNotSaved
Result: showAlertNotifiyingTheUser
What: tapBackButton
Scenario: formIsSaved
Result: goes1ScreenBack
I have no idea how to write unit tests for this! I have added the OCMock framework but it doesn't seem like you're allowed to change a uiviewcontroller's navigation controller since its read-only.
I really want to change my development process to write a failing test first and then code, but this makes it difficult.
Thanks for you time!
I'm not aware of Apple development, but I can comment on standard TDD stuff and that should get you in the right direction - I hope :)
What I'd do for this is have your "Alert" code in a seperate class with an interface, say IAlerter (unless Apple framework already has an interface for "messageboxes/alerts" that you can leverage).
Inject the interface into the constructor of your UI class.. Then in the test, you mock out the IAlerter in the test.
so basically.. (in C#, Moq (mocking library) style, Pseudocode sorry ;))
//ARRANGE
var alerterMock = new Moq.Mock<IAlerter>();//fake the alerter - cuz you really don't want it to happen, you just want to do a "Verify" that it "was" called.
var ui = new UI(alerterMock.Object);
//ACT
ui.StartDataEntry();
ui.GoBack();
//ASSERT
alerterMock.Verify(a => a.ShowAlert(Moq.It.IsAny<string>()));//verify ShowAlert was called with any string - or at least do a lil ".Contains" to make sure it was calling code that shows the text you expect.
HTH :)
Related
I have a hard time to grasp how to implement unit tests in a class where all my fields are private.
The class is calculating a user's position with BLE and CoreLocation - not that important. I have a protocol, which when a new location is found I'm calling it and all the classes which conform to that protocol will receive a room id and room name. So, what that means is that literally all the fields in my class are private, because yeah, there's no reason any outside class should access them right? But that also means I can literally test nothing in that class, even though there are quite a few functions which I would like to test. I mean, I could just make the variables internal instead of private, but it just seems wrong to do that just to unit test. I've heard about dependency injection, but it just seems like so much effort.
For example I have this function:
private var beacons: [AppBeacon] = []
private var serverBeacons:[Beacon] = []
private func addBeacons(serverBeacons: [Beacon]){
for beacon in serverBeacons {
let beacon = AppBeacon(id: beacon.id, uuid: beacon.uuid, building: beacon.building, name: beacon.name)
beacons.append(beacon)
}
}
there's no way I can test whether the beacons array was actually filled up as I wanted to or not for example. The public features of my class are basically a function called startLocating and the result which is the room id and name and I know in black box testing which unit testing imitates (right?) I should not care about the intermediate steps, but honestly, with this much functionality should I just say, doesn't matter? And assume i did populate the beacons with some rssi values of my choice, the actual location algorithm is executed on a node.js server, so I honestly don't know what to test client side?
It's classic MVC and there's no way I can change it architecture until the deadline that I have, so I don't know what's the best way to go from here? Just don't test the functionalities? Make the fields internal instead of private? We do testing of the algorithm itself server side, so testing whether the the room id is the expected room id, is already tested.
I read on another post the following:
"Unit testing by definition is black box testing, which means you don't care about the internals of the unit you test. You are mainly interested to see what's the unit output based on the inputs you give it in the unit test.
Now, by outputs we can assert on several things:
the result of a method
the state of the object after acting on it,
the interaction with the dependencies the object has
In all cases, we are interested only about the public interface, since that's the one that communicates with the rest of the world.
Private stuff don't need to have unit tests simply because any private item is indirectly used by a public one. The trick is to write enough tests that exercise the public members so that the private ones are fully covered.
Also, one important thing to keep in mind is that unit testing should validate the unit specifications, and not it's implementation. Validating implementation details adds a tight coupling between the unit testing code and the tested code, which has a big disadvantage: if the tested implementation detail changes, then it's likely that the unit test will need to be changed also, and this decreases the benefit having unit test for that piece of code."
And from that I essentially understand it as that I should just not unit test this?
If you have a private var that would help you write unit tests, change it to private(set) var so that it can be read (but not changed).
Revealing the innards may bother you. If it does, it's possible that there's another type waiting to be extracted from the view controller.
First, the definition of 'unit-test' you have quoted is quite unusual: All definitions from the literature that I am aware of consider unit-testing a glass-box/white-box testing method. More precisely, unit-testing as such is actually neither black-box or white-box - it is the method used for designing the test cases that makes the difference. But there is no reason not to apply white-box test design techniques for unit-testing. In fact, some white-box test design techniques make only sense when applied for unit-testing. For example, when you investigate on unit-testing, you will encounter a lot of discussions about different code coverage criteria, like, statement coverage, branch coverage, condition coverage, MC/DC - for all of which it is essential to know the implementation details of the code and consider these implementation details during test case design.
But, even taking that into account, it does not change much for your particular case: The variables in your code are still private :-) However, you are approaching the testing problem in a too restrictive way: You try to test the function addBeacons for itself, in total isolation even from the other functions in that component.
To test addBeacons, your test cases should not only contain a call to addBeacons, but also some other function call, the result of which would show you if the call to addBeacons was successfull. For example, if you also have a function to find a beacon by name or position, you would first call addBeacon and, to check that this has succeeded, call one of these function to see if your added beacon will be found. These two calls would be part of the same test case.
As expected, I normally have difficulties when following TDD (i.e. writing the tests first) with the view layer.
Namely, in order to observe or trigger certain visible changes (layout or styling) I would need to make the view's internals public. This breaks encapsulation and allows for client code do some thing like myView.label.text = "User".
To avoid this, I either add getter methods to UIView class:
var userName: String{ return label.text }
Or do some extensions that are added only to the test framework:
extension MyView{
//avoids making a getter just for the sake of testing, while keeping it private and interchangeable
var userName : String{
return (viewWithTag(someLabelTage) as! UILabel).text
}
A different approach is to skip the TDD work flow (i.e. test manually after the feature is done) and add snapshot testing (see https://github.com/pointfreeco/swift-snapshot-testing) to the increase coverage and have a safety net when refactoring.
Considering all this, I was wondering if there are any other patterns or approaches, that I can use to be more efficient while keeping my confidence in the code.
Not an expert in Swift, but regardless of the language/framework, something tells me you're actually making your task harder.
I normally have difficulties when following TDD with the view layer.
This is expected. The View support to be simple and have no behavior (i.e domain logic) at all. It should be simple user interaction, and binding data to the controls in the view. So you don't need TDD or to be more precise Unit Tests around the View in my opinion. You're not going to get much value out of trying to write Unit tests for the View.
You View can be tested more effectively using a UI test framework, such as Selenium, or your own UI test framework, which tests User interactions. This way you more return in investment (ROI) than attempting to TDD the View layer.
I don't have much to add to Spock's answer. Tests should be written to help development and maintenance of the code in the future. If they get in the way then either the code is not architected properly or it's code for which the ROI is low.
A pattern that is worth mentioning is the humble view. There are a few ways to approach it, each with different trade offs, but the gist is to have data structures defining how the view should look like, and then let the view read these data structure and set its properties from them.
This allows you to test that the data structure driving the view is generated properly, without having to test the view itself, because it's nothing but an humble object that reads and sets values.
I've been looking into applying the Model View Presenter architecture to a new iOS project. After some reading, I found that this post had the best example. Link to raw gist of code here.
The bottom of the example has the assembly code:
// Assembling of MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter
Furthermore, the author states:
Since we don’t want the View to know about the Model, it is not right to perform assembly in presenting view controller (which is the View), thus we have to do it somewhere else. For example, we can make the app-wide Router service which will be responsible for performing assembly and the View-to-View presentation.
My question:
Where should I put the assembly code?
Where can I find more example of an app-wide router?
So it's not just me who was wondering about this ;)
Let me share with you my other finding regarding to MVP in iOS:
Screencast by David Gadd - It's a bit long (1h 20min) and a bit old (December 2012) but for sure worth seeing. You will see there how MVP is implemented (in AppCode) for a very small app (alongside with a pretty good description on how to write unit tests). The router in app David is creating is called ServiceLocator. Unfortunately I couldn't find the code to download for this screencast (but it is possible to create your own version of this app while watching it).
Perhaps there are other ways to create router, but this screencast helped me to understand it a bit more. I'm pretty new to MVP concept and I didn't use it in a bigger app (bigger than one-screen-let's-see-how-it's-done-app). It would be great to see how MVP was implemented in a real life app...
[EDIT]
I've just realised I didn't answer your first questions.
According to app in screencast router is a class with one class method:
+ (id)resolve:(PresenterTypeEnum)type;
In implementation of this method you will find simple switch. Basing on type send in a parameter method will return proper instance of a presenter.
This method is called in viewDidLoad. Once you have instance of a presenter you just have to set the view of a presenter with self.
I hope this explanation is clear. Anyway, I highly recommend watching screen cast, then it should be clear as a crystal ;)
I have a class which can be configured to do 2 slightly different things. I want to test both paths. The class is a descendant of UIViewController and most of the configuration takes place in Interface Builder. i need to verify that both Storyboard Scenes and their Outlets are wired up in the same way, but also need to check for the difference in behavior.
I'd like to use shared XCTest suites for this purpose.
One is aimed for use with the left hand, one for the right. Both appear after another when using the app. The first one (right hand) triggers a segue to the other. The last one (left hand) should trigger a different segue. This is where it differs, for example.
Now I want to verify the segues with tests. I'd like to create a BothHandSharedTests suite which both view controller instance tests use to verify everything they have in common. However, the BothHandSharedTests class is treated as a self-containing test suite, which it clearly isn't.
I came up with these strategies:
inherit from an abstract XCTest descendant, like described above (doesn't seem to be that easy),
write a test auite for the common properties and use one of the two as the Object Under Test, and add two smaller suites for the differences.
How would you solve this problem?
Here's a solution in Swift:
class AbstractTests: XCTestCase {
// Your tests here
override func perform(_ run: XCTestRun) {
if type(of: self) != AbstractTests.self {
super.perform(run)
}
}
}
I don't have a conclusive answer, but here's what I ended up doing.
I first tried the subclassing route. In the parent test (the "AbstractTestCase") I implemented all the tests that would be executed by the the AbstractTestCase subclasses, but added a macro so they don't get run by the actual parent test:
#define DONT_RUN_TEST_IF_PARENT if ([[self className] isEqualToString:#"AbstractTestCase"]) { return; }
I then added this macro to the start of every test, like so:
- (void)testSomething
{
DONT_RUN_TEST_IF_PARENT
... actual test code ...
}
This way, in the ConcreteTestCase classes which inherit from AbstractTestCase, all those tests would be shared and run automatically. You can override -setUp to perform the necessary class-specific set-up, of course.
However – This turned out to be a crappy solution for a couple reasons:
It confuses Xcodes testing UI. You don't really get to see a live representation of what's running, and tests sometimes don't show up as intended. This makes clicking through to debug test failures difficult or impossible.
It confuses XCTest itself – I found that tests would often get run even when I didn't ask them too (if I were just trying to run a single test) and the so the test output wouldn't be what I would expect.
Honestly it felt a little janky to have that macro – macros which redirect flow control are never really that good of an idea.
Instead, I'm now using a shared object, a TestCaseHelper, which is instantiated for each test class, and has has a protocol/delegate pattern common to all test cases. It's less DRY – most test cases are just duplicates of the others – but at least they are simple. This way, Xcode doesn't get confused, and debugging failures is still possible.
A better solution will likely have to come from Apple, unless you're interested in ditching your entire test suite for something else.
So I'm starting to catch the TDD bug but I'm wondering if I'm really doing it right... I seem to be writing A LOT of tests.
The more tests the better, sure, but I've got a feeling that I'm over doing it. And to be honest, I don't know how long I can keep up writing these simple repetitive tests.
For instance, these are the LogOn actions from my AccountController:
public ActionResult LogOn(string returnUrl)
{
if (string.IsNullOrEmpty(returnUrl))
returnUrl = "/";
var viewModel = new LogOnForm()
{
ReturnUrl = returnUrl
};
return View("LogOn", viewModel);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(LogOnForm logOnForm)
{
try
{
if (ModelState.IsValid)
{
AccountService.LogOnValidate(logOnForm);
FormsAuth.SignIn(logOnForm.Email, logOnForm.RememberMe);
return Redirect(logOnForm.ReturnUrl);
}
}
catch (DomainServiceException ex)
{
ex.BindToModelState(ModelState);
}
catch
{
ModelState.AddModelError("*", "There was server error trying to log on, try again. If your problem persists, please contact us.");
}
return View("LogOn", logOnForm);
}
Pretty self explanatory.
I then have the following suite of tests
public void LogOn_Default_ReturnsLogOnView()
public void LogOn_Default_SetsViewDataModel()
public void LogOn_ReturnUrlPassedIn_ViewDataReturnUrlSet()
public void LogOn_ReturnUrlNotPassedIn_ViewDataReturnUrDefaults()
public void LogOnPost_InvalidBinding_ReturnsLogOnViewWithInvalidModelState()
public void LogOnPost_InvalidBinding_DoesntCallAccountServiceLogOnValidate()
public void LogOnPost_ValidBinding_CallsAccountServiceLogOnValidate()
public void LogOnPost_ValidBindingButAccountServiceThrows_ReturnsLogOnViewWithInvalidModelState()
public void LogOnPost_ValidBindingButAccountServiceThrows_DoesntCallFormsAuthServiceSignIn()
public void LogOnPost_ValidBindingAndValidModelButFormsAuthThrows_ReturnsLogOnViewWithInvalidModelState()
public void LogOnPost_ValidBindingAndValidModel_CallsFormsAuthServiceSignIn()
public void LogOnPost_ValidBindingAndValidModel_RedirectsToReturnUrl()
Is that over kill? I haven't even shown the services tests!
Which ones (if any) can I cull?
TIA,
Charles
It all depends on how much coverage you need / want and how much dependability is an issue.
Here are the questions you should ask yourself:
Does this unit test help implement a feature / code change that I don't already have?
Will this unit test help regression test/debug this unit if I make changes later?
Is the code to satisfy this unit test non-trivial or does it deserve a unit test?
Regarding the 3rd one, I remember when I started writing unit tests (I know, not the same thing as TDD) I would have tests that would go like:
string expected, actual;
TypeUnderTest target = new TypeUnderTest();
target.PropertyToTest = expected;
actual = target.PropertyToTest;
Assert.AreEqual<string>(expected, actual);
I could have done something more productive with my time like choose a better wallpaper for my desktop.
I recommend this article by ASP.net MVC book author Sanderson:
http://blog.codeville.net/2009/08/24/writing-great-unit-tests-best-and-worst-practises/
I'd say you are doing a little more than you probably have to. While it is nice to test every possible path your code can take, some paths just aren't very important or don't result in real differences in behavior.
In your example take LogOn(string returnUrl)
The first thing you do in there is check the returnUrl parameter and re-assign it to a default value if it is null/empty. Do you really need a whole unit test just to make sure that one line of code happens as expected? It isn't a line likely to break easily.
Most changes that might break that line be things that would throw a compile error. A change in the default value being assigned is possible in that line (maybe you decide later that "/" isn't a good default value... but in your unit test, I bet you hard-coded it to check for "/" didn't you? So the change in the value will necessitates a change in your test... which means you aren't testing your behavior, instead you are testing your data.
You can achieve a test for the behavior of the method by simply having one test that does NOT supply a parameter. That will hit your "set default" part of the routine while still testing that the rest of the code behaves well too.
That looks about right to me. Yes, you will write a lot of Unit Tests and, initially, it will seem like overkill and TBH a waste of time; but stick with it, it'll be worth it. What you should be aiming for (rather than just 100% code coverage) is 100% function coverage. However... if you find that you're writing a lot of UTs for the same method it's possible that that method is doing too much. Try separating your concerns more. In my experience the body of an Action should be doing little more than newing-up a class to do the real work. It's that class that you should really be targeting with UTs.
Chris
100% coverage is very ideal, its really helpful if you have to massively refactor your code however, as the tests will govern your code specs to make sure it is correct.
I am personally not a 100% TDD (sometimes too lazy too) but if you intend to do 100%, maybe you should write some test helpers to take away some burden on these repetitive tests. For example, write a helper to test all your CRUD in a standard post structure with a callback to allow you pass in some evaluation might save you a lot of time.
I'm unit testing only code what i'm unsure about. Sure - you can never know what will back stab you but writing tests for trivial things seems like an overkill for me.
I'm not a unit-testing/tdd guru - but i think that it's fine if you do NOT write tests just to have them. They must be useful. If you are experienced enough with unit testing - you start to feel when they are going to be valuable and when not.
You might like this book.
Edit:
Actually - i just found a quote about this underneath isolation frameworks chapter. It's about overspecifying tests (one particular test), but i guess the idea remains in more global scope:
Overspecifying the tests
If your test has too many expectations, you may create a test that
breaks down with even the lightest of code changes, even though the
overall functionality still works. Consider this a more technical way of
not verifying the right things. Testing interactions is a double-edged
sword: test it too much, and you start to lose sight of the big picture—the overall functionality; test it too little, and you’ll miss the
important interactions between objects.