Reputation: 350
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:
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?
Upvotes: 3
Views: 2230
Reputation: 5939
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.
Upvotes: 0
Reputation: 20980
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.
Upvotes: 3