QUnit returns error in strict mode - unit-testing

I'm trying to create a (pure) constructor function and a QUnit test for it:
//app.js
function myFunc(param1, param2) {
this.param1 = param1;
this.param2 = param2;
return this;
}
//test.js
QUnit.test("should return a string and a number", function(assert) {
assert.ok(myFunc("some-string", 4545435234), "a string and a number were returned");
});
The code runs and passes my test until I add "use strict" to app.js. Then QUnit displays the following fail message:
1. Died on test #1 at http://some/url/:1:1: Cannot set property 'param1' of undefined
Source: TypeError: Cannot set property 'param1' of undefined
I can get both the code to work and the test to pass if I return the myFunc parameters as an array:
function myFunc(param1, param2)) {
return [param1, param2];
}
But that just doesn't seem right. I get that this has something to do with var hoisting but I'm not clear about it.
Thanks in advance.
...

In strict mode JavaScript functions are not given the default context (this), thus you must provide the context. Once way to do this is through the new keyword. If you change your assertion to the following I think this will work:
assert.ok(new myFunc("some-string", 4545435234), "a string and a number were returned");

Related

Testing redux-saga takeEvery

I have the following super simple Redux-Saga that I want to test with Jest.
function* nextApi() {
yield* takeEvery(
(action) => !!(action.meta && action.meta.next),
nextApiSaga
)
}
I've looked at Redux-Sagas-Test-Plan, but that only seems to allow you to unit test functions that contain Saga Effect Creators and doesn't seem to support Saga Helpers. There is also Redux-Saga-Test but that just does a deepEqual on the yielded effect and doesn't test the arrow function.
What I want to be able to do is past the following two objects to takeEvery and see that nextApiSaga is only called in the second case.
{ type: 'foo' }
{ type: 'foo', meta: { next: 'bar' } }
I left you a comment about redux-saga-test-plan having methods for saga helpers, but you can easily test takeEvery with it. Call testSaga with your saga and then invoke the takeEvery method assertion with the pattern (note I keep a reference to your original anonymous function) and the other saga.
const helper = action => !!(action.meta && action.meta.next)
function* nextApi() {
yield* takeEvery(
helper,
nextApiSaga
)
}
testSaga(nextApi).takeEvery(helper, nextApiSaga)
Taking a different approach, I came up with this. Not sure if it's the best answer, but it seems to work. Adding here in case anyone else has the same problem and still open to better suggestions.
function getTakeEveryFunction(saga) {
return saga().next().value.TAKE.pattern
}
it('takes actions with meta.next property', () => {
const func = getTakeEveryFunction(nextApi)
expect(func({ type:'foo' })).toBe(false)
expect(func({ type:'foo', meta: { next: 'bar' } })).toBe(true)
})

Testing against early returns (defensive programming)

I am learning testing and trying to test a function using 'early returns'. The function on success sets a property in another class and on failure simply returns so in both cases it 'returns' void.
class Test
{
private $fileHandler;
private $config;
public __constructor($fileHandler, $config)
{
$this->fileHandler = $fileHandler;
$this->config = $config;
}
public function example($filePath)
{
$exists = $this->fileHandler->exists($filePath);
if ($exists === false) {
return;
}
$this->config->set($filePath);
}
}
In this example I believe I can test this with two unit tests and by mocking the fileHandler class.
For a failure (early return) the $config class's method set() should not be called whilst for a success the method should be called.
However, this test passes if I try and change never() to once() making me think the entire test is bogus.
/** test */
public function config_is_not_set_with_missing_file()
{
$fileHandlerMock = $this->getMockBuilder(fileHandler::class)->getMock;
$fileHandlerMock->method('exists')
->willReturn('false');
$configMock = $this->getMockBuilder(config::class)->getMock;
$test = new Test($fileHandlerMock, $configMock);
$test->example('fake file path');
$configMock->expects($this->never())
->method('set');
}
Your file handler mock is returning the string 'false', which is !== to false. Change that to false and Tets::example should return early.
You're not passing the $configMock to the Test constructor, so it's not being used.
You're right, if the test passes both with once and never expectations, the test is not working as expected and requires reviewing it.

Testing Ember.Logger.error assertions

I'm using Ember.Logger.error:
if (isInvalid) {
Ember.Logger.error('this is invalid');
}
I want to test it in qunit:
assert.throws(() => myFunction(), /this is invalid/, 'error was thrown');
But assert.throws doesn't catch the error. It does if I replace Ember.Logger.error with a simple throw statement, but surely there's a way to test for logged Ember errors. Anyone know the way?
UPDATE:
I made a little addon that adds this ability to QUnit. You can get it here.
Okay, so I've done a research how it's done in Ember and I've seen what's the practice to test it:
ember.js/packages/ember/tests/helpers/link_to_test.js
ember.js/packages/ember/tests/routing/basic_test.js
Here's example test function you could use to test calling Ember.Logger.error in helper unit test:
/* global Ember */
import { demo } from '../../../helpers/demo';
import { module, test } from 'qunit';
module('Unit | Helper | demo');
test('catching log', function(assert) {
assert.expect(1); // define how many assertions we expect
const oldError = Ember.Logger.error; // store original error function in variable
Ember.Logger.error = function(message) { // monkey patch with our custom function
assert.equal(message, 'this is invalid', 'error was thrown'); // our assertion
};
demo(); // actually call function
Ember.Logger.error = oldError; // restore original error function
});

Trying to expose additional information when using xUnit Assert.Throws

I'm just setting up some first unit tests, and I can't quite see how I'm trying to achieve (with my current test structure) can be done, which means I'm not sure whether my approach to the tests is incorrect, or it's just a limitation on xUnit.
I'm testing my MVC Controllers, and want to ensure that they all provide a ArgumentNullException if they are constructed passing null across as the arguments (they get resolved via Castle in the real world).
So, I've a private field on the Test class:
private IEnumerable<Type> ControllerTypes = typeof(MyBaseController).Assembly.GetTypes().Where(t => IsController(t));
Then, my test method:
[Fact]
public void EnsureControllersThrowIfProvidedWithNull() {
foreach (var controller in ControllerTypes) {
var ctrs = GetConstructorsForType(controller);
if (null == ctrs || !ctrs.Any()) { //if the controller has no constructors, that's fine, we just skip over it
continue;
}
var ctr = ctrs.ElementAt(0);
var ctrParamsAsNull = ctr.GetParameters().Select(p => (object)null);
Assert.Throws<ArgumentNullException>(() => {
ctr.Invoke(ctrParamsAsNull.ToArray());
});
}
}
So this is all working fine, I run the test runner, and one of my Controllers doesn't throw an ArgumentNullException when passed null, great, my test fails, but I don't know which controller it was, from the given output.
I do know how I can debug through the test to see which it is that fails, and can manually go through all my controllers to check which it is, but it would be useful to know which controller it was that failed.
Or am I just using a unit test wrong here?
(Side note, there's another test which ensures there's only 1 public constructor for each controller, so I can be sure I'm targeting the correct constructor when this fires, as long as that first test passed).
Thanks
Note:
There's a flaw in the logic for the test, which means it doesn't fully cover what I was expecting it too, as long as it throws an ArgumentNullException for at least 1 of the arguments, then it will pass the test, which isn't right. However as the arguments are interfaces I can't instantiate a new instance of them. So anyone looking to copy the code for the test, I wouldn't do so. Not looking for a solution to that issue here.
Assert.Throws is only helper method that executes delegate inside try catch block. You don't have to use it and you can replace it with your own implementation. Something like:
[Fact]
public void EnsureControllersThrowIfProvidedWithNull() {
foreach (var controller in ControllerTypes) {
var ctrs = GetConstructorsForType(controller);
if (null == ctrs || !ctrs.Any()) { //if the controller has no constructors, that's fine, we just skip over it
continue;
}
var ctr = ctrs.ElementAt(0);
var ctrParamsAsNull = ctr.GetParameters().Select(p => (object)null);
book ok = false;
try
{
ctr.Invoke(ctrParamsAsNull.ToArray());
}
catch(ArgumentNullException)
{
//you get exception you expected so continue
ok = true;
}
if(!ok)
{
// you didn't get exception so throw your own exception with message that contains controller type name
throw new Exception(String.Format("Ctor on type {0} did not throw ArgumentNullException",controller.Name);
}
}
}
This is only as idea to work on. You can refactor that inside your own static assertion method...

Moq tests using ExpectSet() with It.Is<T>() aren't behaving as... expected

I've isolated the behaviour into the following test case. I'd be grateful to anyone who can tell me how to expect/verify a property set for a List<T> property - it appears there's something going on inside It.Is<T>(predicate) that isn't making a whole lot of sense to me right now. Sample code will run as a console app from VS2008 - you'll need to add a reference to Moq 2.6 (I'm on 2.6.1014.1) - please try uncommenting the different ExpectSet statements to see what's happening...
using System;
using Moq;
using System.Collections.Generic;
namespace MoqDemo {
public interface IView {
List<string> Names { get; set; }
}
public class Controller {
private IView view;
public Controller(IView view) {
this.view = view;
}
public void PopulateView() {
List<string> names = new List<string>() { "Hugh", "Pugh", "Barney McGrew" };
view.Names = names;
}
public class MyApp {
public static void Main() {
Mock<IView> mockView = new Mock<IView>();
// This works - and the expectation is verifiable.
mockView.ExpectSet(mv => mv.Names);
// None of the following can be verified.
// mockView.ExpectSet(mv => mv.Names, It.Is<Object>(o => o != null));
// mockView.ExpectSet(mv => mv.Names, It.Is<List<string>>(names => names.Count == 3));
// mockView.ExpectSet(mv => mv.Names, It.IsAny<IList<String>>());
Controller controller = new Controller(mockView.Object);
controller.PopulateView();
try {
mockView.VerifyAll();
Console.WriteLine("Verified OK!");
} catch (MockException ex) {
Console.WriteLine("Verification failed!");
Console.WriteLine(ex.Message);
}
Console.ReadKey(false);
}
}
}
}
I'm not using the very latest version of Moq, so I don't have an overload of ExpectSet that takes two parameters, but I've had some success with this pattern:
mockView.ExpectSet(mv => mv.Names).Callback(n => Assert.That(n != null));
The Assert (from NUnit) call in the callback will throw an exception if the value assigned to .Names doesn't match the predicate. It does make it hard to trace when a test fails, though. I agree that the ability to pass an It.Is or It.IsAny as the second parameter would be handy.
The second parameter of ExpectSet() is the value you're expecting. You can't use It.Is<T> in this case as there's no overload that takes a predicate - though it would be nice ;) Here's a (simplified) excerpt from your sample, illustrating the use of a value:
var mockView = new Mock<IView>();
var list = new List<string> { "Hugh", "Pugh", "Barney McGrew" };
mockView.ExpectSet(mv => mv.Names, list);
mockView.Object.Names = list;
Hope that helps.
Edit: fixed typo.
BTW, It.Is is not supported on ExpectSet. Your code compiles just because they are regular method invocations when used as values (as opposed to expressions), whereas when used in an Expect expression they are pre-processed by Moq and given specific meaning (rather than the null/default value that all It.Is members actually return).
You could use the stub behavior on the given property (mockView.Stub(mv => mv.Names)) and later assert directly for its value after execution.
Moq doesn't provide an overload receiving It.IsAny as it's effectively the same as calling ExpectSet without passing an expected value ;)