Unit testing Nativescript application logic on a browser - unit-testing

I am trying to set up unit testing for a Nativescript application, run by ng test on a browser. The problem is that whenever there is a tns-core-modules or another plugin import, the module cannot be resolved because of the platform specific files (e.g. "tns-core-modules/application/application.android.js") that never get compiled into the bundle, thus throwing an error like "Module not found: Error: Can't resolve 'tns-core-modules/application'".
I know there is a built-in unit test support in Nativescript. The problem I have with it is that it can't run on CI. I would like to be a ble to have lightweight tests for my business logic, mocking out all platform dependencies.
I have looked for a way to mock the module imports at runtime with no luck. I looked into rewire package but it only runs on node.

I finally managed to get it working. Not a very elegant solution and I have yet to see how much maintenance it requires. Key points here:
Use paths section of the tsconfig.json to add mock import
locations
In the mocks directory create files for any unresolved module
Some nativescript modules are referencing helper functions on global
scope but they're undefined. My solution was to define them in
test.ts like this
window['__decorate'] = () => {};
window['__extends'] = () => {};
window['__metadata'] = () => {};
window['__param'] = () => {};
window['layout_base_1'] = { CSSType: () => {} };
window['Crashlytics'] = {};
window['Fabric'] = {};

You simply can not run NativeScript application on Browser.
In case if you are looking for something like headless mode, Appium has one too, isHeadless in capabilities.
Between, may I know why you think you can not run the {N} unit tests on CI? It should work on CI too, after all it's a machine that runs the same commands based on some trigger.

Related

jest manual ES6 class mock is not active and I want to understand why

I am having problems using Jest manual mocks (the one in a parallel __mocks__ directory) in my project.
I think I understand how to use it and it actually works fine if I remove a single line in a file specified in the Jest setupFiles array.
In that file a global helper is installed (into global.createComp) that uses the vuex store.
This is a vue + vuex project but even running the stripped down spec using only jest gives unexpected results.
Can somebody look at my minimal reproducible example repo at https://github.com/thenoseman/jest-manual-mock-not-working, do a npm i and npm run test:unit and help me understand why the mock is not active?
You can find the line that need to be commented out in test/unit/support/helpers.js.
Also the README shows a screenshot and further explains what the problem looks like.
setupFiles are evaluated before test files. As the reference states,
A list of paths to modules that run some code to configure or set up the testing environment. Each setupFile will be run once per test file. Since every test runs in its own environment, these scripts will be executed in the testing environment immediately before executing the test code itself.
JavaScript modules are evaluated once on first import. Importing #/store/modules/internetAtHome in helpers.js results in importing original #/api/DslService.
The mock in test file doesn't affect #/api/DslService because it has already been evaluated earlier:
jest.mock("#/api/DslService");
import DslService from "#/api/DslService";
In case helpers.js needs mocked #/api/DslService, jest.mock needs to be moved there.
In case helpers.js needs original #/api/DslService but tests need mocked one, the module (and any module that depends on it) needs to be re-imported with jest.resetModules or jest.isolatedModules:
jest.mock('#/api/DslService');
let DslService;
jest.isolateModules(() => {
DslService = require("#/api/DslService").default;
});
...
For a module that was imported with original implementation and needs to be re-imported as a mock, jest.requireMock can be used, it doesn't need jest.mock('#/api/DslService'):
let DslService = jest.requireMock("#/api/DslService").default;
...

Web Unit Tests not finding Url

I am using aspnetboilerplate 5.1.0.
In the ProjectName.Web.Tests I have run into a situation that I cannot solve.
I have set up web tests for my controller using [Fact] or [Theory].
When I attempt to run the tests using GetResponseAsString(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) found in the webtestbase class. All the tests fail.
Here is an example of my Test:
[Fact]
public async Task Index_Test()
{
//Act
var response = await GetResponseAsStringAsync(
GetUrl<HomeController>(nameof(HomeController.Index))
);
//Assert
response.ShouldNotBeNullOrEmpty();
}
The Tests all fail on this:
Message:
Shouldly.ShouldAssertException : response.StatusCode
should be
HttpStatusCode.OK
but was
HttpStatusCode.NotFound
I have other aspnetboilerplate projects in version 3.8.3 and 4.2.1 and the web tests work just fine. So I'm not sure why the server is not able to find the action methods on my controllers.
The service tests found in the ProjectName.Tests project run just fine.
I found the culprit. The problem I was experiencing was due to attempting to copy a project for web unit tests from one of the aspnetboilerplate project template repositories and updating all of the references and class names to match the names and namespaces in the destination VS solution.
I submitted a similar question on the aspnetboilerplate github account.
https://github.com/aspnetboilerplate/aspnetboilerplate/issues/5463.
Ultimately, here is what happened.
After going through the same process with a newer project. I found that In the
class file that would by default be named AbpProjectNameWebTestBase.cs in the method
protected override IWebHostBuilder CreateWebHostBuilder()
{
return base
.CreateWebHostBuilder()
.UseContentRoot(ContentRootFolder.Value)
.UseSetting(WebHostDefaults.ApplicationKey, typeof(AbpProjectNameWebModule).Assembly.FullName);
}
I mistakenly replaced AbpProjectNameWebModule with AbpProjectNameTestModule instead of AbpProjectNameWebMvcModule. This was trying to use the Application Service Unit test project as the web project. Therefore it could not find any of the referenced URI's and therefore returned httpStatusCode.NotFound.
After fixing this reference. I started getting exceptions that pertained to the public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) method.
These were things like adding app.UseAuthentication() and app.UseAuthorization() as well as needing to add a Middleware to provide a ClaimsIdentity and ClaimsPrincipal for the context.User (i.e. app.UserMiddleware<TestAuthenticationMiddleware>())
Now, I am able to get my web unit tests to run as I had in previous versions.

ReferenceError: ga is not defined [Ionic 2.2 Unit Testing With Karma]

I'm adding unit tests to an Ionic 2.2.0 app I manage, but my Components crash at test-time when they encounter Google Analytics code. I'm using Ionic's official unit testing example as a basis, and my current progress can be seen on our public repo.
My project uses Google Analytics, which is added to the HTML and downloaded at runtime (because we have different keys for development vs production).
The code that initializes Analytics is in my main.ts, and it sets a global variable ga, which is subsequently available throughout the application.
I'm beginning the tests for the app's first page, which uses Analytics. When I run the tests, I'm met with the following error
Component should be created FAILED
ReferenceError: ga is not defined
at new MyBusesComponent (webpack:///src/pages/my-buses/my-buses.component.ts:33:6 <- karma-test-shim.js:138419:9)
at new Wrapper_MyBusesComponent (/DynamicTestModule/MyBusesComponent/wrapper.ngfactory.js:7:18)
at CompiledTemplate.proxyViewClass.View_MyBusesComponent_Host0.createInternal (/DynamicTestModule/MyBusesComponent/host.ngfactory.js:15:32)
........
This is because main.ts doesn't seem to be loaded or executed, and I assume TestBed is doing that purposefully. It's certainly better that I don't have the actual Google Analytics object, but the Component does need a function called ga.
My question, therefore, is as follows: how can I create Google Analytics' ga variable in my test configuration such that it's passed through to my components at test-time?
I've tried exporting a function from my mocks file and adding it to either the imports or providers arrays in my spec file, but to no avail.
I appreciate any advice! Feel free to check my code at our repo I linked to above and ask any followups you need. Thanks!
You declare the var ga but that is just to make TypeScript happy. At runtime, the ga is made global from some external script. But this script is not included in the test.
What you could do is just add the (mock) function to the window for the tests. You could probably do this in your karma-test-shim.js.
window.ga = function() {}
Or if you wanted to test that the component is calling the function with the correct arguments, you could just add the function separately in each test that uses the function. For example
beforeEach(() => {
(<any>window).ga = jasmine.createSpy('ga');
});
afterEach(() => {
(<any>window).ga = undefined;
})
Then in your test
it('..', () => {
const fixture = TestBed.creatComponent(MyBusesComponent);
expect(window.ga.calls.allArgs()).toEqual([
['set', 'page', '/my-buses.html'],
['send', 'pageview']
]);
})
Since you're making multiple calls to ga in the constructor, the Spy.calls will get the argument of all each call and put them in separate arrays.

AngularJS mocking $logProvider in config block

Is there a way to inject providers when writing unit tests using Karma(Testacular) and Jasmine in angular?
Our team decided recently to use angularjs $log to write debugging details to the console. This way we can leverage the ability to disable the logging via the $logProvider.debugEnabled() method.
angular.module("App", ["prismLogin", "ui.bootstrap"])
.config(["$routeProvider", "$logProvider",
function ($routeProvider, $logProvider) {
$routeProvider
//routes here edited for brevity
//This is the offending line, it breaks several pre-existing tests
$logProvider.debugEnabled(true);
}]);
However after adding the $logProvider.debugEnabled(true); line several of our tests no longer execute successfully, failing with the following message:
TypeError: Object doesn't support property or method 'debugEnabled' from App
So my question again, is it possible to mock the $logProvider? Or should I provide my own configuration block for the test harness?
I attempted searching for a way to mock the app module with no luck. It seems to me that using the concrete app module instead of a mock is very brittle. I would like to avoid reworking tests associated with the app module every time a change is made in the app or run configuration blocks.
The tests that are failing are units of code with no relation to the $logProvider? I feel as if I a missing something here and making things much harder than they should be. How should one go about writing tests that are flexible and are not affected by other side effects introduced in your application?
It appears that this is a know issue with angular-mocks.
Until the issue is addressed , I was able to resolve the issue by adding the following method to the angular.mock.$LogProvider definition in angular-mocks.js at line 295.
this.debugEnabled = function(flag) {
return this;
};

MvvmCross: Unit-testing services with plugins

I am creating a cross-platform project with MvvmCross v3 and Xamarin solution and i would like to create some unit-tests.
This seems a bit outdated, so i was trying to follow this and it worked as expected.
However, I am now making an attempt to unit-test some of my domain services, which are dependent on platform specific MvvvCross plugins (e.g ResourceLoader).
Running the test results in the following exception:
Cirrious.CrossCore.Exceptions.MvxException: Failed to resolve type
Cirrious.CrossCore.Plugins.IMvxPluginManager.
I assume that IMvxPluginManager is probably registered in the Setup flow, and that I need to include platform implementation of the plugins in my project, yet I was wondering what would be the preferred way of setting up my unit-test project? Is there something that I am missing?
Is there any updated tutorial for the above task?
Are there already any plugin platform extensions that supports test environment, or should I make an attempt to write them by myself?
In general, you shouldn't be loading the plugins or a real MvxPluginManager during your service tests.
Instead your unit tests should be registering mock types for the interfaces that your services need to use.
var mock = new Mock<INeedToUse>();
// use mock.Setup methods
Ioc.RegisterSingleton<INeedToUse>(mock.Object);
// or you can use constructor dependency injection on INeedToUse instead
You can also register a mock IMvxPluginManager if you really need to, but in the majority of cases I don't believe you should need that. If you've got a case where you absolutely need it, please post a code sample - it's easier to talk in code than text.
This scenario should be well possible. I wanted to UnitTest my SqlLite service implementation. I did the following to get it to work:
Create a Visual Studio unit test project
Add a reference to .Core portable library project
Add a nuget reference To MvvmCross Test Helper
Add a nugget reference to MvvmCross SqlLite Plugin
( this will make use of the WPF implementation of SqlLite)
Download the SqlLite windows library and copy these into your test project
Sql Lite Download location
And make sure to add the sqllite3.dll to the root of your unit test project and set the "Copy to Output Library" to "Copy always". This will make sure the actual sqllite database is copied to the unit test dll location. (Check that the DLL is copied to your bin/debug folder)
Then write you unit test the following way:
[TestClass]
public class SqlServiceTests:MvxIoCSupportingTest
{
private readonly ISQLiteConnectionFactory _factory;
public SqlServiceTests()
{
base.ClearAll();
_factory = new MvxWpfSqLiteConnectionFactory();
Ioc.RegisterSingleton<ISQLiteConnectionFactory>(_factory);
}
[TestMethod]
public void YourSqlLiteTest()
{
// Arrange
var si = new SqlDataService(_factory);
var list = si.GetOrderList();
}
}
I haven't tested this with my viewmodel. By using the IoC.RegisterSingleton method the SqlConnectionFactory should be readyli available for your viewmodels.