Proper use of reasons - fluent-assertions

My test code has the following assert:
testSubscriber.Called.Should().BeTrue("the handler was not called");
When it fails I get the following error message:
Expected True because the handler was not called, but found False.
English is not my native language, but this does not sound right, what should I write in the reason?

The reason should be the reason why your assertion should pass. In your case, it appears you instead wrote the reason it would fail.
That parameter will be directly substituted into failure message. It will make sure not to repeat the word "because", so you can include that in the string which may make the code read more clearly.
Regarding the English for this particular case, the exact language I would use would depend on the situation.
If you are asserting that calling the handler sets Called to true, you might say case:
testSubscriber.Called.Should().BeTrue("because the handler was called");
which would result in the message
Expected True because the handler was called, but found False.
If you are confident that calling the handler will set Called to true, and you are instead trying to assert that the handler was called:
testSubscriber.Called.Should()
.BeTrue("we expected the handler to have been called");
which would result in the message
Expected True because we expected the handler to have been called, but found False.

Related

How to use OCMock to verify that an asynchronous method does not get called in Objective C?

I want to verify that a function is not called. The function is executed in an asynchronous block call inside the tested function and therefore OCMReject() does not work.
The way I have tested if async functions are indeed called would be as follows:
id mock = OCMClassMock([SomeClass class]);
OCMExpect([mock methodThatShouoldExecute]);
OCMVerifyAllWithDelay(mock, 1);
How would a test be done to test if a forbidden function is not called?
Something like:
VerifyNotCalled([mock methodThatShouoldExecute]);
OCMVerifyAllWithDelay(mock, 1);
I would recommend using an OCMStrictClassMock instead of the OCMClassMock (which gives you a nice mock). A strict mock will instantly fail your test if any method is called on it that you did not stub or expect, which makes your tests a lot more rigorous.
If that's not an option for you, you can do what you described with:
OCMReject([mock methodThatShouoldExecute]);
See the "Failing fast for regular (nice) mocks" section in the OCMock docs.
Now as for waiting for your code which may call the forbidden method, that's another matter. You can't use OCMVerifyAllWithDelay since that returns immediately as soon as all expectations are met, it doesn't wait around a full second to see if illegal calls will be made to it. One option is to put a 1 second wait before verifying the mock each time. Ideally, you could also wait explicitly on your asynchronous task with an XCTestExpectation. Something like:
XCTestExpectation *asyncTaskCompleted = [self expectationWithDescription:#"asyncTask"];
// Enqueued, in an onCompletion block, or whatever call
// ... [asyncTaskCompleted fulfill]
[self waitForExpectationsWithTimeout:1 handler:nil]

OCMock and overriding stub value

mockModule = OCMPartialMock(module);
OCMStub([mockModule send:#"FOO"]).andReturn(YES);
OCMStub([mockModule send:#"FOO"]).andReturn(NO);
In this example I have a simple mock module, and I set some stubs to return YES/NO when sent a String, the problem that occurs is that if I set the same string twice it only returns the first value, and not the new value.
In this example about the problem is demonstrated like so I would expect a call such as:
BOOL answer = [module send:#"FOO"]
//answer should be NO, but is YES
How can I make it respond with the most recently set value?
You could use the expect methods, e.g.
mockModule = OCMPartialMock(module);
OCMExpect([mockModule send:#"FOO"]).andReturn(YES);
OCMStub([mockModule send:#"FOO"]).andReturn(NO);
That's not exactly what they are meant for, but it does make some sense. You're basically saying, I expect that send: will be called, and when that has actually happened, then I want the method to be stubbed.
Also, if it were possible to "pile up" the stubs, figuring out what went wrong would be quite difficult, e.g. if the first invocation of the stub doesn't happen, then the second invocation will get the value meant for the first.

Fail safe assertions in Swift

I commonly use assertions in Objective-C where I want to assert a value. On a debug build I assert in order to stop execution of the program and check if my assumption was incorrect. However, on production builds I find a way to fail safely in a way to minimise the user impact. I achieve this by creating a macro that encapsulates an NSAssert within an if statement which also executes the code I would like to run as a failsafe on production. For example:
An assertion macro I would use:
#define AssertTrueOrExecute(condition, action) \
if (!condition) { \
NSAssert(testCondition, #"Condition failed"); \
action; \
}
Somewhere in my application I would have something like this:
- (void)someMethod
{
BOOL testCondition = ...
// Ensure the testCondition is true before proceeding any further
AssertTrueOrExecute(testCondition, return);
// Potentially unsafe code that never gets executed if testCondition is false
}
- (void)someReturningMethod
{
BOOL testCondition = ...
// Ensure the testCondition is true before proceeding any further
AssertTrueOrExecute(testCondition, return #"safe string");
// Potentially unsafe code that never gets executed if testCondition is false
}
Since I cannot define a macro like the one mention in Swift, is there a way to have the same behaviour? That is how would I go about having a Swift equivalent for my AssertTrueOrExecute macro?
Update:
To further explain the question, if I was using Swift I currently would write something like this:
func someMethod () {
let testCondition : Bool = ...
// Ensure the testCondition is true before proceeding any further
if (!testCondition) {
assert(testCondition);
return;
}
// Potentially unsafe code that never gets executed if testCondition is false
}
So the question is more along the lines of how can the if statement with the assertions be wrapped in a similar way I have the Objective-C macro so that I can assert or return early for example?
Update 2:
Another example would be in function that returns something, for example:
func someReturningMethod () -> String {
let testCondition : Bool = ...
// Ensure the testCondition is true before proceeding any further
if (!testCondition) {
assert(testCondition);
return "safe string";
}
// Potentially unsafe code that never gets executed if testCondition is false
return "some other string"
}
There are no macros in Swift, but there could be other means in Swift where you could achieve this same functionality as it is possible in Objective-C.
However, the real issue here is, that you try to approach a problem in a way which you really shouldn't:
Do not mix programmer errors and runtime errors!
Instead, make a clear distinction what programmer errors are and what runtime errors are. Handle programmer errors with assertions, and handle runtime errors with NSError respectively in Swift with try & catch and throw.
Note, that the "scope" of a programmer error is not restricted to the point when the program fails through an assertion failure: Very likely such an error has bad side effects which leave the program in an invalid state, and often this assertion detects errors that may have occurred possibly a long time before the assertion failed. So, when an assertion fails, your program is very likely already in an invalid state.
A rule of thumb is, that an assertion failure should not happen in production code (read MUST NOT). Well, these are programmer errors and should be fixed, shouldn't they? You verify your assumptions using assertions in unit tests. If you still fear, that your assumption may break in production and are also sure that this is not a runtime error (which should always be handled gracefully), it should stop the program - all bets are off anyway. In Swift, you can use fatalError for this.
Sometimes, the distinction whether the violation of a certain assumption is a programmer error or whether it's a runtime error is not always that obvious and may depend on the context. As a programmer, though, you can always define what it is. Take a string parameter as example: if you obtain it directly from a text field from a user input who wants to create an account and is asked for his name, you should validate the string and return/throw an error if it doesn't fit your expectation - for example if it is empty, too short etc. That is, in Swift you may throw an error and handle that gracefully on the call-site, possibly in a View Controller. On the other hand, you define that it would not make sense to initialise a User object whose name will be empty. That is, in your init routine you define the precondition that a valid user name must not be empty, and you check this with assert or fatalError. In this scenario your program is correct, when there is no code path which initialises a User whose name is empty.

OCMock report error from stub

I want to test that my code properly handles a particular error returned from dataWithContentsOfFile. The problem is that the error isn't specifically returned from the method, it is an output parameter (passed in as NSError **.
It is easy enough for me to simply have the mock return nil from the stub, but I want to specifically test that the error it outputs is handled. How can I achieve that?
I think what you're looking for is described in section 2.5 (Returning values in pass-by-reference arguments) in the documentation: http://ocmock.org/reference/#stubing-methods

Enforcing to supply a completion block

I'm wondering about whether or not this is good practice:
I have a method that takes in parameters and a callback block, let's say something along the lines of:
-(void)loginWithUsername:(NSString *)username andPassword:(NSString *)password withCompletion:(LoginManagerCompletionBlock)completionHandler;
Now in this specific case, there is no use in calling this method without a completion handler, as it triggers a redundant call to a login web service (also, it does not change the state of anything - not client side nor server side). I would like to avoid these situations by actively enforcing the requirement of passing me a completion block in order to make this web service call. I sort of think of this as a "#required" method in an Objective C protocol. So my questions are:
Is requiring a completion block in order to perform an action good practice in Objective C?
(Edit: Answered) How do I enforce this requirement? Is there built-in language syntax that can help me out here?
Thanks
You can use the function attribute nonnull(params), where params is 1 or more comma-separated parameter numbers, to indicate that a parameter should not be null (nonnull without parentheses means all pointer parameters should not be null). For your example:
- (void) loginWithUsername:(NSString *)username
andPassword:(NSString *)password
withCompletion:(LoginManagerCompletionBlock)completionHandler
__attribute__((nonnull(3)));
However, while this is a compile time check and produces a warning it will only do so if null is passed directly. If the argument value is an expression which evaluates to null, e.g. a variable with a null value, then this will not be caught.
If a parameter being null is an error you can add a runtime check within the method itself using NSParameterAssert(parameter), where parameter is the name of one of the method's parameters, to check for this condition. This call is defined to print an error message and throw an exception if its argument evaluates to false, and null evaluates to false.
This is exactly what NSParameterAssert is for. Use it to check that parameters aren't nil.
NSParameterAssert( completionParameter );
Though, in this specific case, it's probably best to log the completion handler being nil and return. There is no point doing any additional work. You probably want the assertion during development to make it obvious that you have an issue that needs to be resolved.