Using NUnit to test HTTP status of a WebFaultException - unit-testing

I want to write a unit test to ensure that I get a WebException with a 404 status code thrown from a particular method.
The WebException bit is easy:
[Test]
[ExpectedException(typeof(WebFaultException))]
public void CheckForWebFaultException()
{
var myClass = new MyClass();
myClass.MyMethod();
}
However this could be a 404, a 400, 401 or any other of the myriad of other http codes.
I could do a try/catch/Assert.True but this feels like a hack. Is there a way to Assert against a property of the thrown exception?
Something like
Assert.Throws(typeof(WebFaultException), myClass.MyMethod(), wfx => wfx.StatusCode == HttpStatusCode.NotFound);

I was on the right lines, Assert.Throws actually returns the exception which was thrown.
[Test]
public void CheckForWebFaultException()
{
var myClass = new MyClass();
var ex = Assert.Throws<WebFaultException>(() => myClass.MyMethod());
Assert.AreEqual(HttpStatusCode.NotFound, ex.StatusCode);
}
Note that I've taken out the [ExpectedException(typeof(WebFaultException))] as the exception is now handled and the test will fail if this is left in.
Assert.Throws ensures that the exception was thrown by myClass.MyMethod() and the second assert checks the status code.

Related

Mock Ktor's http client with MockK

I want to mock requests with ktor's http client using MockK. The problem is all the methods related to making requests with the client are inline, so I cannot use coEvery on those methods. The next thing I tried was to go through the called methods until I found a method that wasn't inline and then mock that. After stepping through some functions, the HttpClient.request() function instantiates an HttpStatement and then calls execute() on it.
public suspend inline fun HttpClient.request(
builder: HttpRequestBuilder = HttpRequestBuilder()
): HttpResponse = HttpStatement(builder, this).execute()
If I can mock the constructor and .execute() functions, I can intercept the call and return my canned response. I can then check that the builder's params are correct inside of a verify function.
mockkConstructor(HttpStatement::class)
coEvery { anyConstructed<HttpStatement>().execute() } returns mockk {
coEvery { status } returns HttpStatusCode.OK
coEvery { body<RefreshToken>() } returns RefreshToken()
}
This code takes care of intercepting the execute call. The next step would be to verify the constructor params of HttpStatement. This code to verify execute was called works:
coVerify { anyConstructed<HttpStatement>().execute() }
Next thing is to verify the constructor params. This pull request in the MockK repo describes how to verify constructors:
coVerify { constructedWith<HttpStatement>(/* Matchers here */).execute() }
Note that I have to add the .execute() or else MockK tells me I'm not verifying anything.
Missing calls inside verify { ... } block.
io.mockk.MockKException: Missing calls inside verify { ... } block.
at app//io.mockk.impl.recording.states.VerifyingState.checkMissingCalls(VerifyingState.kt:52)
at app//io.mockk.impl.recording.states.VerifyingState.recordingDone(VerifyingState.kt:21)
...
Ok, so just add in the matchers. However, no combination of matchers I try works. I've tried doing a bunch of constant matchers for type Any (which should match anything right?)
coVerify { constructedWith<HttpStatement>(ConstantMatcher<Any>(true))}
I've tried a matcher for HttpRequestBuilder and HttpClient
coVerify {
constructedWith<HttpStatement>(
ConstantMatcher<HttpRequestBuilder>(true),
ConstantMatcher<HttpClient>(true)
).execute()
}
And a whole slew of others. Each time, I get this error:
Verification failed: call 1 of 1: HttpStatement(mockkConstructor<HttpStatement>(any(), any())).execute(any())) was not called
java.lang.AssertionError: Verification failed: call 1 of 1: HttpStatement(mockkConstructor<HttpStatement>(any(), any())).execute(any())) was not called
at io.mockk.impl.recording.states.VerifyingState.failIfNotPassed(VerifyingState.kt:63)
at io.mockk.impl.recording.states.VerifyingState.recordingDone(VerifyingState.kt:42)
...
Next thing I figured I could try would be to use an answers block earlier on in order to print out the types of the parameters being passed in case I was wrong, but that also runs into the "nothing being done in every block" error.
coEvery { anyConstructed<HttpStatement>() } answers {
args.filterNotNull().map { it::class.qualifiedName }.forEach(::println)
mockk {
coEvery { execute().status } returns HttpStatusCode.OK
coEvery { execute().body<RefreshToken>() } returns RefreshToken(
accessToken = accessToken,
expiresIn = expiresIn,
)
}
}
Is there a solution to mocking the http client? Do I have to mock something even more internal? Or do I just have to stick to using the ktor MockEngine?

Redux Saga: Throw and stop generator

I'm writing a generator. I'm testing it with RITEway. It checks if window.ethereum is defined. If its not, it should throw and stop. Basically it should satisfy the following tests:
describe('handle initialize Web3 saga', async assert => {
global.window = {}
assert({
given: 'nothing, the window object',
should: 'have no property called Web3',
actual: window.web3,
expected: undefined
})
const gen = cloneableGenerator(handleInitializeWeb3)()
{
// The important parts are in this block scope
const clone = gen.clone()
assert({
given: 'window.ethereum undefined',
should: 'throw',
actual: clone.next().value.message,
expected: '[WARNING]: window.ethereum has no provider!'
})
assert({
given: 'nothing',
should: 'be done',
actual: clone.next().done,
expected: true
})
}
class Provider {}
window.ethereum = new Provider()
// ... more tests
})
Here is how I tried implementing it.
function* handleInitializeWeb3() {
if (!window.ethereum) {
yield new Error('[WARNING]: window.ethereum has no provider!')
}
// ... more yields
}
but this saga doesn't stop. The test where it should: 'be done' fails and the saga gives back the values from the yields outside of the if statement. How can I have these tests pass and the saga stop when the error is thrown?
yielding an error instance acts the same as yielding any other value (i.e. the generator keeps running). If you want to stop the generator you should throw new Error(... like in a normal function.
If for some reason you don't want to throw and do in fact want to yield an error instance and then stop, simply return; after you've yielded the error.

PHPUnit test code inside catch block

I have a Symfony controller using try...catch.
I use phpunit to test my application. I have searched but havent found a way how to test the code inside a catch exception. How can I force php unit to pretend that something went wrong and enters the catch block and test this as well?
ie:
try {
$foo = 1;
} catch (\Exception $ex) {
$mail = new Mail();
$mail->sendMail();
return new Response();
}
How can I tell phpunit to throw an \Exception so it will test code inside catch block of above?
Well, under those conditions, it will obviously not throw any exceptions, but consider the function your try/catch lies within. You need to unit test that function, and provide arguments that will cause it to fail, and catch.
For instance:
public function doStuff($argument) {
try {
$parsed = (int)$argument; //but what if $argument is a string with letters
} catch (\Exception $ex) {
//do stuff
}
To test that an exception is thrown when you mess it up:
public function testDoStuff() {
// get a mock of the class, let's just call it $mock
// do some regular asserts if you want
$this->setExpectedException('\Exception');
$mock->doStuff("haha, you can't parse this");
}
If you really have some complex stuff in your catch block you can move it to separate protected method of the controller and test it separately. You can easily access protected method outside of its class using reflection.

NSubstitute conditions for throwing exception other than parameters

I'm using NSubstitute to mock a class that my method under test uses. This mocked class may throw a particular exception under certain conditions.
The method that I'm testing has some "retry" logic that it executes when it catches this exception. I'm trying to test this retry logic. So, I need a particular method of this mocked class to throw the exception sometimes, but not other times. Unfortunately, the method that throws this exception has no parameters, so I can't base the throw logic on parameters.
How can I make the mocked object's method throw the exception either:
A) ...the first N times it's called
or
B) ...based on the parameters some other method that's called before it
or
C) ...under any other condition other than the parameters passed in
To give you a clearer picture of what I'm trying to do, my code is something like:
IDataSender myDataSender = GetDataSender();
int ID = GetNextAvailableID();
myDataSender.ClearData();
myDataSender.Add(ID,"DataToSend");
bool sendSuccess = false;
while (!sendSuccess)
{
try
{
myDataSender.SendData();
sendSuccess = true;
}
catch (IDCollisionException)
{
ID++;
MyDataSender.ClearData();
myDataSender.Add(ID,"DataToSend");
}
}
So, I need to test my retry logic, and I need to simulate that IDCollisionException. However, I can't have the SendData() throwing the exception every single time, or the retry loop will never succeed.
What can I do here?
If I understand the question correctly, you can use When..Do and close over a local variable to get this behaviour.
const int throwUntil = 3;
var callsToSendData = 0;
var dataSender = Substitute.For<IDataSender>();
dataSender
.When(x => x.SendData())
.Do(x =>
{
callsToSendData++;
if (callsToSendData < throwUntil)
{
throw new DbCollisionException();
}
});
Similarly, you can also use callbacks to locally capture parameters passed to other methods, and access them within the Do block (rather than just using a counter).

Node assert.throws not catching exception

Given this code:
var assert = require('assert');
function boom(){
throw new Error('BOOM');
}
assert.throws( boom(), Error );
I get this output, with node 0.4.9:
node.js:134
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: BOOM
at boom ([EDITED]/assert.throws.test.js:4:9)
at Object.<anonymous> ([EDITED]/assert.throws.test.js:7:17)
at Module._compile (module.js:402:26)
at Object..js (module.js:408:10)
at Module.load (module.js:334:31)
at Function._load (module.js:293:12)
at Array.<anonymous> (module.js:421:10)
at EventEmitter._tickCallback (node.js:126:26)
This, to me, implies that an uncaught exception has occurred, as opposed to a reported, caught exception. Looking in the docs, I notice that the examples look more like this:
var assert = require('assert');
function boom(){
throw new Error('BOOM');
}
assert.throws( boom, Error );
But how do you test if it throws an exception given a certain input? For example:
var assert = require('assert');
function boom(blowup){
if(blowup)
throw new Error('BOOM');
}
assert.throws( boom, Error );
This will fail. What am I doing wrong, or what secret does everybody know but me?
The examples take a function, while your sample code calls a function and passes the result. The exception happens before the assert even gets to look at it.
Change your code to this:
var assert = require('assert');
function boom(){
throw new Error('BOOM');
}
assert.throws( boom, Error ); // note no parentheses
EDIT: To pass parameters, just make another function. After all, this is javascript!
var assert = require('assert');
function boom(blowup){
if(blowup)
throw new Error('BOOM');
}
assert.throws( function() { boom(true); }, Error );
You can use bind():
assert.throws( boom.bind(null), Error );
With arguments it is:
assert.throws( boom.bind(null, "This is a blowup"), Error );
Current node stable (v4.1) includes fat arrow function support by default (no --harmony flag required) so you can do something like:
assert.throws(()=>boom(), Error);
assert.throws(()=>boom(true), Error); // with params
Even if you have parentheses after boom() (so you're actually invoking it, instead of passing a reference to the function object), by using the fat arrow function you're wrapping it in a block, which is what assert.throws expects.
This is closely related to the issue people with with other assertion Mocha/Chai. See this answer for the description with node examples:
Mocha / Chai expect.to.throw not catching thrown errors