RxJs 5 ascii marbles output for failing test - unit-testing

I am learning RxJS 5 (release candidate). I am using the ASCII marble diagrams in unit tests.
For a failing test, is there a built in function to show the actual ASCII marbles string instead of the deep equal failure result? It seems like if I have to understand and write an expected ASCII marble string that it would be useful for a failing test to show the actual ASCII marble string. It would assist in troubleshooting.
I am hoping that there is a built in function that is effectively the reverse of parseMarbles(). I have not found anything yet when searching through the documentation and source code.
Below is an example taken from Writing marble tests - anatomy of a test that I modified to get a failing test. It gives the following output:
AssertionError: expected [ Array(5) ] to deeply equal [ Array(4) ]Error
I would rather see something like:
AssertionError: expected "'---(be)----c-f-----|'"
to equal "'---(be)----c-------|'"
http://jsbin.com/hipepew/edit?js,output
mocha.setup('bdd');
const expect = chai.expect;
describe('RXJS 5 Marbles', () => {
it("basic anatomy of a failing test", () => {
const rxTestScheduler: TestScheduler = new Rx.TestScheduler(function (actual, expected) {
expect(actual).to.deep.equal(expected);
});
const e1 = rxTestScheduler.createHotObservable<string>('----a--^--b-------c--|');
const e2 = rxTestScheduler.createHotObservable<string>('---d-^--e---------f-----|');
const expectedFail = '---(be)----c-------|';//missing f at frame 130
rxTestScheduler.expectObservable(e1.merge(e2)).toBe(expectedFail);
rxTestScheduler.flush();
});
});
mocha.run();

Related

Dart unit test giving inconsistent results

I have a fairly simply unit test which has been working without issue for a long time.
It has suddenly started failing.
When I run/debug the unit test in vscode the test succeeds.
When I run the test from the cli it succeeds on windows but not on linux.
dart run test -j1 test/src/functions/ask_test.dart
Which is kind of interesting as there is nothing windows/linux specific.
The test has also been working for a long time on linux.
test('ask.any - throws', () {
final validator = Ask.any([
Ask.fqdn,
Ask.ipAddress(),
Ask.inList(['localhost'])
]);
expect(
() => validator.validate('abc'),
throwsA(predicate<AskValidatorException>((e) =>
e is AskValidatorException && e.message == 'Invalid FQDN.')));
});
The test gives the following error:
Error: Expected: throws satisfies function
Actual: <Closure: () => String>
Which: threw AskValidatorException:<Invalid FQDN.>
stack package:dcli/src/functions/ask.dart 760:7 _AskValidatorAny.validate
test/src/functions/ask_test.dart 65:25 main.<fn>.<fn>
package:test_api expect
test/src/functions/ask_test.dart 64:5 main.<fn>
As you can see validate method is doing exactly what is expected (throwing).
The problem seems to be that the unit test frame work is not seeing it as a match.
This is the validator which is throwing as expected.
class _AskFQDN extends AskValidator {
const _AskFQDN();
#override
String validate(String line) {
final finalLine = line.trim().toLowerCase();
if (!isFQDN(finalLine)) {
throw AskValidatorException(red('Invalid FQDN.'));
}
return finalLine;
}
}
I'm using test 1.17.5 on Dart 2.13.
The code is from the DCli package which you can find here:
https://github.com/bsutton/dcli
The specific unit test is here
https://github.com/bsutton/dcli/blob/master/test/src/functions/ask_test.dart
I am not sure this will solve your problem, but you can make your assertion a bit cleaner if you do it like this:
expect(
() => validator.validate('abc'),
throwsA(isA<AskValidatorException>().having((e) =>
e.message == 'Invalid FQDN.')));

Stencil, Leaflet, unit testing component, gives TypeError: Cannot read property 'deviceXDPI' of undefined

So we are developing a Stenciljs component which wraps leaflet map and adds some additional functionality.
Now obviously we don't want or need to test Leaflet, but instead, just the parts in our wrapper components.
So, using the test examples, we create our tests,
import { LeMap } from "./le-map";
describe("Map component tests", () => {
it("Should build the map component", async () => {
const map = new LeMap();
expect(map).not.toBeNull();
});
});
try and load the components and test the public functions, but we get
TypeError: Cannot read property 'deviceXDPI' of undefined
> 1 | import {Component, Element, Listen, Method, Prop, Watch} from
'#stencil/core';
> 2 | import L from 'leaflet';
| ^
3 |
4 | #Component({
5 | shadow: false,
We believe this message is because the test is trying to render leaflet, and because it's not a true browser, it can't detect a view so throwing this error, so we've tried to mock leaflet in the tests, but still get the same problem.
We're tried to mock the leaflet module by using jest mocking
jest.genMockFromModule('leaflet');
but this made no diffrence
Only idea I've had is to separate the logic from the components, but that feels wrong, as we'd just be doing this for purpose of testing.
Versions in use are: leaflet: 1.3.4, #stencil: 0.15.2, jest: 23.4.2
Any other suggestions?
Further investigation with, thanks to #skyboyer 's suggestions, leads me to this line of the leaflet core browser.js file
leads me to this line of the leaflet core browser.js file
export var retina = (window.devicePixelRatio || (window.screen.deviceXDPI/window.screen.logicalXDPI)) > 1;
But I'm unable to mock the screen property of window as I get the following error
[ts] Cannot assign to 'screen' because it is a constant or a read-only property,
so I try the following.
const screen = {
deviceXDPI:0,
logicalXDPI:0
}
Object.defineProperty(window, 'screen', screen);
Object.defineProperty(window, 'devicePixelRatio', 0);
Same error, completely ignores this, so I try over riding the export.
jest.spyOn(L.Browser,'retina').mockImplementation(() => false);
No joy either, so tried
L.Browser.retina = jest.fn(() => false);
but get it tells me it's a constant and can't be changed (yet the implication stats var so ¯_(ツ)_/¯ )
Anything else I can try?
Further update,
I've managed to mock the window, but this sadly doesn't solve it.
const screenMock = {
deviceXDPI: 0,
logicalXDPI: 0
}
const windowMock = {
value: {
'devicePixelRatio': 0,
'screen': screenMock
}
}
Object.defineProperty(global, 'window', windowMock);
If I console log this, I get the right properties but as soon as I test the instantiation of the component it fails with
TypeError: Cannot read property 'deviceXDPI' of undefined
Reading around it seems Leaflet doesn't check for a DOM and just tries to render anyway, I can't see anyway around this, I saw a leaflet-headless package, but I don't know how I could swap them out just for testing.
Think I will need to look at another strategy for testing, probably protractor.
Found a solution, not fully tested yet, but the tests pass.
I did it by creating a
__mocks__
directory at the same level as the node_modules directory.
created a file called leaflet.js in it. It's a simple file it just contains.
'use strict';
const leaflet = jest.fn();
module.exports = leaflet;
then in my test file (le-map.spec.ts) I just added
jest.mock('leaflet')
before the imports
and now my test passes.
I tried doing this in the test itself but that just gave me the same error, it must be something in the loading sequence which means it has to be manually mocked beforehand.
Hope this helps others, it's been driving me mad for weeks.

Jest expect().toEqual() not throwing error

I expected the following sample test to fail if I use toEqual(), but it passes:
it('sample test to check toEqual and toBe', () => {
const expectedAction = {test: '123', value: undefined};
const actualAction = {test: '123'};
expect(expectedAction).toEqual(actualAction);
})
The same test fails if I use .toBe(). However, if I fix the test case like this:
it('sample test to check toEqual and toBe', () => {
const expectedAction = {test: '123', value: '123'};
const actualAction = {test: '123', '123'};
expect(expectedAction).toBe(actualAction);
});
it again fails saying that "Compared values have no visual difference".
Is the behaviour correct? How to modify my test case so that it fails correctly.
If you really need it to be considered as different, one option would be to use Jest snapshot test, like:
expect(expectedAction).toMatchSnapshot();
The first time, it will create a snapshot file and print the javascript object to it, when you run the test again, it will compare it to the snapshot.
Most of the times, the snapshots are used to compare the component rendered tree, but you can use it for any javascript object.
The first one passes cause toEqual compares every element of your objects with each other, so in your case
expectedAction.test === actualAction.test
as both are '123'
but the same is true for
expectedAction.value === actualAction.value
as both are undefined
The second test fails cause toBe uses === to compare the two objects which will of cause fail as they are not the same instances. The only way that an object will passes toBe would be to use itself for comparison:
expect(expectedAction).toBe(expectedAction)

Chai.js not specifying which assertion is failing in a test

I'm using mocha with chai.js for CoffeeScript unit testing. I have a grunt task to compile the coffee files to the test folder and start PhantomJS to run the mocha tests.
Everything works fine however chai.js only indicates what test is failing and what are the expected and actual values, it does not specify what assertion is not passing in a test case. Is there any good way to print the assertion or at least the index of the assertion that is failing? I also turned on chai.Assertion.includeStack however that only shows the line number in the JavaScript file that is not that helpful for the CoffeeScript tests.
FizzBuzzTest.coffee
fizz = new FizzBuzz()
describe "Print numbers from 1 to 100", ->
it "First 10 digits from 1 to 5", ->
result = fizz.do()
arr = result.split(fizz.Delimiter)
expect(arr[0]).to.equal("1")
expect(arr[1]).to.equal("2")
expect(arr[2]).to.equal(fizz.Fizz)
expect(arr[3]).to.equal("3") # this assertion should fail
expect(arr[4]).to.equal(fizz.Buzz)
Executing tests
$ grunt test
Running "mocha:run" (mocha) task
Testing: Content/runner.html
Print numbers from 1 to 100
✓ First 10 digits from 1 to 5
1) Last 10 digits from 95 to 100
✓ Print FizzBuzz instead of number which is divisible by both 3 and 5
✓ Check number
✓ Check Fizz
✓ Check Buzz
✓ Check FizzBuzz
✓ Check if > 100 then null
✓ Check if < 1 then null
8 passing (113ms)
1 failing
1) Print numbers from 1 to 100 Last 10 digits from 95 to 100:
AssertionError: expected true to be false
at file:///Users/milan/Sites/CoffeeTests/Content/js-libs/chai/chai.js:918
at file:///Users/milan/Sites/CoffeeTests/Content/js-libs/chai/chai.js:1159
at file:///Users/milan/Sites/CoffeeTests/Content/js-libs/chai/chai.js:3563
>> 1/9 tests failed (0.11s)
Warning: Task "mocha:run" failed. Use --force to continue.
Aborted due to warnings.
Question:
Is there a good way to set chai.js to be more specific about where the AssertionError happened?
Like: AssertionError: expected true to be false in line expect(arr[3]).to.equal("3")
Thank you.
If no one comes up with a more sophisticated solution to make chai.Assertion.includeStack work nicely with CoffeeScript code, you can always pass messages to some of the functions in the expect interface:
it("bluh", function () {
chai.expect(false).to.be.equal(true, "here");
});
The "here" string is a message that will be output with the error message when the assertion fails. Take a look at the documentation though because not all functions take an optional message. I first wanted to use to.be.true above but the true method does not take an optional message.
The assert interface supports optional messages on more functions than the expect interface does.

Challenges of refactoring unit-tests to be maintainable and readable when dealing with List<T> objects

In the book The Art of Unit Testing it talks about wanting to create maintainable and readable unit tests. Around page 204 it mentions that one should try to avoid multiple asserts in one test and, perhaps compare objects with an overridden Equals method. This works great when we have only one object to compare the expected vs. actual results. However what if we have a list (or collection) of said objects.
Consider the test below. I have more than one assert. In fact, there are two separate loops calling asserts. In this case I will end up with 5 assertions. 2 to check the contents of one list exist in another, and 2 vice versa. The 5th comparing the number of elements in the lists.
If anyone has suggestions to improve this test, I'm all ears. I am using MSTest at the moment, though I replaced MSTest's Assert with NUnits for the fluent API (Assert.That).
Current Refactored Code:
[TestMethod]
#if !NUNIT
[HostType("Moles")]
#else
[Moled]
#endif
public void LoadCSVBillOfMaterials_WithCorrectCSVFile_ReturnsListOfCSVBillOfMaterialsThatMatchesInput()
{
//arrange object(s)
var filePath = "Path Does Not Matter Because of Mole in File object";
string[] csvDataCorrectlyFormatted = { "1000, 1, Alt 1, , TBD, 1, 10.0, Notes, , Description, 2.50, ,A",
"1001, 1, Alt 2, , TBD, 1, 10.0, Notes, , Description, 2.50, ,A" };
var materialsExpected = new List<CSVMaterial>();
materialsExpected.Add(new CSVMaterial("1000", 1, "Alt 1", "TBD", 1m, 10.0m, "Notes", "Description", 2.50m,"A"));
materialsExpected.Add(new CSVMaterial("1001", 1, "Alt 2", "TBD", 1m, 10.0m, "Notes", "Description", 2.50m,"A"));
//by-pass actually hitting the file system and use in-memory representation of CSV file
MFile.ReadAllLinesString = s => csvDataCorrectlyFormatted;
//act on object(s)
var materialsActual = modCSVImport.LoadCSVBillOfMaterials(filePath);
//assert something happended
Assert.That(materialsActual.Count,Is.EqualTo(materialsExpected.Count));
materialsExpected.ForEach((anExpectedMaterial) => Assert.That(materialsActual.Contains(anExpectedMaterial)));
materialsActual.ForEach((anActualMaterial) => Assert.That(materialsExpected.Contains(anActualMaterial)));
}
Original Multi-Assert Unit-Test:
...
//1st element
Assert.AreEqual("1000", materials[0].PartNumber);
Assert.AreEqual(1, materials[0].SequentialItemNumber);
Assert.AreEqual("Alt 1", materials[0].AltPartNumber);
Assert.AreEqual("TBD", materials[0].VendorCode);
Assert.AreEqual(1m, materials[0].Quantity);
Assert.AreEqual(10.0m, materials[0].PartWeight);
Assert.AreEqual("Notes", materials[0].PartNotes);
Assert.AreEqual("Description", materials[0].PartDescription);
Assert.AreEqual(2.50m, materials[0].UnitCost);
Assert.AreEqual("A", materials[1].Revision);
//2nd element
Assert.AreEqual("1001", materials[1].PartNumber);
Assert.AreEqual(1, materials[1].SequentialItemNumber);
Assert.AreEqual("Alt 2", materials[1].AltPartNumber);
Assert.AreEqual("TBD", materials[1].VendorCode);
Assert.AreEqual(1m, materials[1].Quantity);
Assert.AreEqual(10.0m, materials[1].PartWeight);
Assert.AreEqual("Notes", materials[1].PartNotes);
Assert.AreEqual("Description", materials[1].PartDescription);
Assert.AreEqual(2.50m, materials[1].UnitCost);
Assert.AreEqual("A", materials[1].Revision);
}
I frequently have more than one assertion. If it's all part of testing one logical unit of work, I don't see any problem with that.
Now, I do agree that if you've got a type which overrides Equals, that makes tests much simpler than your second form. But in your first test, it looks you really just want to assert that the resulting collection equals an expected one. I think that's logically one assertion - it's just that currently you're performing multiple mini-assertions to test it.
Some unit test frameworks have methods to test whether two collections are equal - and if the one you're using doesn't, you can easily write one. I recently did exactly this in my "reimplementing LINQ to Objects" blog series, because although NUnit provides a helper method, its diagnostics aren't terribly helpful. I refactored the code from MoreLINQ very slightly, basically.
This is the refactoring I'm currently using. I overrode the ToString() method of CSVMaterial and added a more useful assert message. So I think this helps with code readability and maintainability. It also makes the unit test trustworthy (due to the helpful diagnostic message).
And Jon, thanks for the thought about a logical unit of work. My refactored code does about the same thing as the previous iteration. Both still test one logical thing. Also, I'll have to look into the MoreLINQ stuff. If it's in your C# InDepth 2nd edition book, I'll come across it as I bought the MEAP version from Manning. Thanks for your help.
public void LoadCSVBillOfMaterials_WithCorrectCSVFile_ReturnsListOfCSVBillOfMaterialsThatMatchesInput()
{
//arrange object(s)
var filePath = "Path Does Not Matter Because of Mole in File object";
string[] csvDataCorrectlyFormatted = { "1000, 1, Alt 1, , TBD, 1, 10.0, Notes, , Description, 2.50, ,A",
"1001, 1, Alt 2, , TBD, 1, 10.0, Notes, , Description, 2.50, ,A" };
var materialsExpected = new List<CSVMaterial>();
materialsExpected.Add(new CSVMaterial("1001", 1, "Alt 1", "TBD", 1m, 10.0m, "Notes", "Description", 2.50m,"A"));
materialsExpected.Add(new CSVMaterial("1001", 1, "Alt 2", "TBD", 1m, 10.0m, "Notes", "Description", 2.50m,"A"));
//by-pass actually hitting the file system and use in-memory representation of CSV file
MFile.ReadAllLinesString = s => csvDataCorrectlyFormatted;
//act on object(s)
var materialsActual = modCSVImport.LoadCSVBillOfMaterials(filePath);
//assert something happended
//Setup message for failed asserts
var assertMessage = new StringBuilder();
assertMessage.AppendLine("Actual Materials:");
materialsActual.ForEach((m) => assertMessage.AppendLine(m.ToString()));
assertMessage.AppendLine("Expected Materials:");
materialsExpected.ForEach((m) => assertMessage.AppendLine(m.ToString()));
Assert.That(materialsActual, Is.EquivalentTo(materialsExpected),assertMessage.ToString());
}