Kotlin-multiplatform: How to execute iOS unit tests - unit-testing

I'm working on a Kotlin-multiplatform library for Android and iOS. I want to write some platform-specific unit test. The tests run as expected for the shared code and Android but not for iOS.
Below the build.gradle file of the shared code module.
apply plugin: "kotlin-multiplatform"
kotlin {
targets {
final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
? presets.iosArm64 : presets.iosX64
fromPreset(iOSTarget, 'iOS') {
compilations.main.outputKinds('FRAMEWORK')
}
fromPreset(presets.jvm, 'android')
}
sourceSets {
commonMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common"
}
commonTest.dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
androidMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
}
androidTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
}
iOSMain.dependencies {
}
iOSTest.dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
}
}
// workaround for https://youtrack.jetbrains.com/issue/KT-27170
configurations {
compileClasspath
}
task packForXCode(type: Sync) {
final File frameworkDir = new File(buildDir, "xcode-frameworks")
final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'
inputs.property "mode", mode
dependsOn kotlin.targets.iOS.compilations.main.linkTaskName("FRAMEWORK", mode)
from { kotlin.targets.iOS.compilations.main.getBinary("FRAMEWORK", mode).parentFile }
into frameworkDir
doLast {
new File(frameworkDir, 'gradlew').with {
text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$#\n"
setExecutable(true)
}
}
}
tasks.build.dependsOn packForXCode
and the structure of the SharedCode module is:
└── src
├── commonMain
│ └── kotlin
├── commonTest
│ └── kotlin
├── androidMain
│ └── kotlin
├── androidTest
│ └── kotlin
├── iOSMain
│ └── kotlin
└── iOSTest
└── kotlin
The tests added in the androidTest and commonTest folders do run as expected but the ones added in the iOSTest do not run.
However, if I replace the the line fromPreset(iOSTarget, 'iOS') { compilations.main.outputKinds('FRAMEWORK') } for fromPreset(presets.macosX64, 'macos') and update the directory names accordly, the tests in the macosTest folder do run as expected.
Why it is not possible to run iOS test when building iOS frameworks? Any idea about what I'm doing wrong or how I can make this works? :)

Currently the kotlin-multiplatform plugin supports only running tests for host platforms (e.g. macOS or Windows). But you can manually add a task for executing iOS tests on a simualtor:
task iosTest {
def device = project.findProperty("iosDevice")?.toString() ?: "iPhone 8"
dependsOn 'linkTestDebugExecutableIos'
group = JavaBasePlugin.VERIFICATION_GROUP
description = "Runs tests for target 'ios' on an iOS simulator"
doLast {
def binary = kotlin.targets.ios.binaries.getExecutable('test', 'DEBUG').outputFile
exec {
commandLine 'xcrun', 'simctl', 'spawn', device, binary.absolutePath
}
}
}
See the full build script here.

As I ran into some issues, I'll post my solution here.
With Kotlin 1.3.50 and XCode 11 I had to change my command line arguments:
val iosTest: Task by tasks.creating {
val device = project.findProperty("iosDevice")?.toString() ?: "iPhone 8"
val testExecutable = kotlin.targets.getByName<KotlinNativeTarget>("iosX64").binaries.getTest("DEBUG")
dependsOn(testExecutable.linkTaskName)
group = JavaBasePlugin.VERIFICATION_GROUP
description = "Runs tests for target 'ios' on an iOS simulator"
doLast {
exec {
println(testExecutable.outputFile.absolutePath)
commandLine( "xcrun", "simctl", "spawn", "--standalone", device, testExecutable.outputFile.absolutePath)
}
}
}
tasks.getByName("allTests").dependsOn(iosTest)

The answer from #IlyaMatveev works perfect for me. But I had to updates two lines using Kotlin Version 1.3.41:
dependsOn 'linkTestDebugExecutableIos'
is now
dependsOn 'linkDebugTestIos'
def binary = kotlin.targets.ios.binaries.getExecutable('test', 'DEBUG').outputFile
is now def binary = kotlin.targets.ios.binaries.getTest("DEBUG").outputFile

Related

How to run database setup only once from multiple Go packages?

I'm trying to create some objects in my database, so that my tests can have some data to work with. I've put my setup logic into a package testsetup. However, I've discovered that go test runs each package as a totally separate instance, so that even though I'm using sync.Once in my testsetup package, Setup still runs multiple times because each package's tests run as a separate Go instance. I really want to keep running my tests in parallel because it's a lot faster, so I'm not currently considering turning off parallelization. Is there a clean way I can do this?
I'm even starting to consider dirty hacks at this point, like using a shell script to implement os-level synchronization.
Here's my package structure:
testsetup
testsetup.go
package1
package1.go
package1_test.go
package2
package2.go
package2_test.go
And here's a simplified version of my testsetup function:
var onceSetup sync.Once
var data model.MockData
func Setup() model.MockData {
onceSetup.Do(createData)
return data
}
func createData() {
// Do some SQL calls to create the objects. We only want to do this once.
data = model.Data{
Object1: ...,
Object2: ...,
}
}
It can be done but it may not be worth it, you'll have to decide that for yourself.
You'll need a package that implements a "test registry" and a "test runner", and another package that is the "entrypoint" that ties it all together and starts the runner.
The resulting structure could look something like this:
../module
├── app
│   ├── pkg1
│   │   ├── foo.go
│   │   ├── ...
│   │   └── tests
│   │   ├── test_foo.go
│   │   ├── ...
│   │   └── pkg1_test.go
│   └── pkg2
│   ├── ...
│   ├── bar.go
│   └── tests
│   ├── ...
│   ├── test_bar.go
│   └── pkg2_test.go
├── go.mod
├── internal
│   └── testutil
│   ├── registry.go # the test registry
│   └── runner.go # the test runner
└── tests
└── start_test.go # the test entrypoint
First, let's consider what the entrypoint will look like once this is done. It may be that you don't like what you see, in that case you should probably ignore the rest of the answer.
File module/tests/start_test.go:
package tests
import (
"testing"
// Use the blank identifier for "side-effect-only" imports
_ "module/app/pkg1/tests"
_ "module/app/pkg2/tests"
// ...
"module/internal/testutil"
)
func Test(t *testing.T) {
testutil.TestAll(t)
}
Next, the registry in module/internal/testutil/registry.go:
package testutil
import (
"path/filepath"
"runtime"
"testing"
)
// v: the directory of a package
// v: the files in a directory
// v: the tests in a file
var tests = make(map[string][][]func(*testing.T))
func Register(ft ...func(*testing.T)) int {
// Use the directory of the Caller's file
// to map the tests. Why this can be useful
// will be shown later.
_, f, _, _ := runtime.Caller(1)
dir := filepath.Dir(f)
tests[dir] = append(tests[dir], ft)
// This is not necessary, but a function with a return
// can be used in a top-level variable declaration which
// can be used to avoid unnecessary init() functions.
return 0
}
The runner in module/internal/testutil/runner.go:
package testutil
import (
"testing"
)
func TestAll(t *testing.T) {
// TODO setup ...
defer func() {
// TODO teardown ...
}()
// run
for _, dir := range tests {
for _, file := range dir {
for _, test := range file {
test(t)
}
}
}
}
Now the individual packages, e.g. module/app/pkg1/tests/test_foo.go:
package tests
import (
"testing"
"module/internal/testutil"
)
var _ = testutil.Register(
TestFoo1,
TestFoo2,
)
func TestFoo1(t *testing.T) {
// ...
}
func TestFoo2(t *testing.T) {
// ...
}
That's it, you can now go to the module/tests "entrypoint" and run:
go test
ADDENDUM #1
If you want to retain the ability to test the individual packages separately
then that can be integrated as well.
First, add a new function to the runner in module/internal/testutil/runner.go:
package testutil
import (
// ...
"path/filepath"
"runtime"
)
// ...
func TestPkg(t *testing.T) {
// Now the directory of the Caller's file
// comes in handy. We can use it to make
// sure no other tests but the caller's
// will get executed.
_, f, _, _ := runtime.Caller(1)
dir := filepath.Dir(f)
// TODO setup ...
defer func() {
// TODO teardown ...
}()
// run
for _, file := range tests[dir] {
for _, test := range file {
test(t)
}
}
}
And in the individual test package add a single test file, e.g. module/app/pkg1/tests/pkg1_test.go:
package tests
import (
"testing"
"module/internal/testutil"
)
func Test(t *testing.T) {
testutil.TestPkg(t)
}
That's it, now you can cd into module/app/pkg1/tests and run:
go test
ADDENDUM #2
Now, with the individual packages having their own _test.go file, you are back to square one if you want to use go test module/... to execute all the tests in the module, since that would not only run the entrypoint but also cause the individual test packages to be executed individually.
You can work around that problem with a simple environment variable however. Just a small adjustment to the testutil.TestPkg function:
package testutil
import (
// ...
"os"
)
// ...
func TestPkg(t *testing.T) {
if os.Getenv("skippkg") == "yes" {
return
}
// ...
}
And now...
# ... the following will work as you'd expect
skippkg=yes go test module/...
go test module/tests
go test module/app/pkg1/tests
Is there some sort of blocking mechanism in your testsetup? I would think that each package would run its tests in parallel still and run what they need from testsetup in parallel. Otherwise you could make it like this:
testsetup
testsetup.go
packages_test.go
package1
package1.go
package2
package2.go
And then in testpackage/packages_test.go, is where you run your tests, importing the code in package1 and package2
It could look something like this:
package testpackage
import (
p1 "project/root/package1"
p2 "project/root/package2"
)
func TestPackages(t *testing.T) {
setup := Setup()
t.Parallel()
t.Run("Package1Test", func(t *testing.T) { package1Test(t, setup) })
t.Run("Package2Test", func(t *testing.T) { package2Test(t, setup) })
}
func package1Test(t *testing.T, d model.MockData) {
err := p1.RunYourFunc(d.data)
require.NoError(t, err)
}
func package2Test(t *testing.T, d model.MockData) {
err := p2.OtherFunc(d.data)
require.NoError(t, err)
}

Rcpp: cannot access module class inside R code of the same package

Let's say I want to build a very simple package in R that wraps c++ code.
My test project would be called bananas.
Let's say I have a folder called "bananas", where inside I have two other folders one called c++ and one called R.
Inside the c++ folder I have folder called include that contains the bananas.hpp header file (with the class definition):
#ifndef BANANAS_H
#define BANANAS_H
class Bananas
{
public:
void add_banana();
int get_bananas();
protected:
int number_of_bananas;
};
#endif
Outside include there is the bananas.cpp file that implements the methods of bananas.hpp:
#include "include/bananas.hpp"
using namespace std;
void Bananas::add_banana(){
// Return False if edge already existed
number_of_bananas ++;
}
int Bananas::get_bananas(){
return number_of_bananas;
}
now in my R folder I have a the wrapper.cpp file that uses Rcpp library to export c++ classes inside R as a module:
#include <Rcpp.h>
#include "include/bananas.hpp"
using namespace Rcpp;
RCPP_EXPOSED_CLASS(Bananas)
RCPP_MODULE(Bananas_cpp){
class_<Bananas>("BananasCPP")
.default_constructor()
.method("add_banana", &Bananas::add_banana)
.method("get_bananas", &Bananas::get_bananas)
;
}
Note that right now #include "include/bananas.hpp" does not mean anything, but in the future this file will be added inside source.
Finally I have my main concern which a R wrapper of this class under:
require(R6)
library(Rcpp)
# For exposing the error
print(ls(all.names = TRUE))
# Load Module
bcpp <- Module("Bananas_cpp", PACKAGE="RBananasC", mustStart = TRUE)$BananasCPP
Bananas <- R6Class("Bananas", list(
bn = new(bcpp),
print_bananas = function() {print(self$bn$get_bananas())},
banana_tree = function(d) {
for(row in 1:d){
self$bn$add_banana()
}
}
))
Now when running the setup.R file in my main directory which looks as follows:
require(Rcpp)
# Make a base package
unlink("RBananasC", recursive=TRUE)
Rcpp.package.skeleton(name = "RBananasC", list = character(),
path = ".", force = FALSE,
cpp_files = c("bananas/R/wrapper.cpp", "bananas/c++/bananas.cpp", "bananas/c++/include/bananas.hpp"))
dir.create("RBananasC/src/include")
file.rename("RBananasC/src/bananas.hpp", "RBananasC/src/include/bananas.hpp")
install.packages("RBananasC", repos=NULL, type="source")
# See that Bananas_cpp works
library(RBananasC) #Without this line it doesn't work
print(Module("Bananas_cpp", PACKAGE="RBananasC", mustStart = TRUE)$BananasCPP)
bcpp <- Module("Bananas_cpp", PACKAGE="RBananasC", mustStart = TRUE)$BananasCPP
ban <- new(bcpp)
ban$add_banana()
print(ban$get_bananas())
# Make the desired package
unlink("RBananas", recursive=TRUE)
Rcpp.package.skeleton(name = "RBananas", list = character(),
path = ".", force = FALSE,
code_files = c("bananas/R/bananas.R"), cpp_files = c("bananas/R/wrapper.cpp", "bananas/c++/bananas.cpp", "bananas/c++/include/bananas.hpp"))
dir.create("RBananas/src/include")
file.rename("RBananas/src/bananas.hpp", "RBananas/src/include/bananas.hpp")
install.packages("RBananas", repos=NULL, type="source")
I have a very strange behaviour inside my bananas.R file, that has to do with the fact that Bananas_cpp module of my package is not visible and so I cannot access the class BananasCPP upon installation.
On the other hand if I ignore the file bananas.R I can import the BananasCPP from the module Bananas_cpp of the package RBananasC, I created using Rcpp.package.skeleton.
To sum up the total file structure looks like:
.
├── bananas
│   ├── c++
│   │   ├── bananas.cpp
│   │   └── include
│   │   └── bananas.hpp
│   └── R
│   ├── bananas.R
│   └── wrapper.cpp
└── setup.R
And to demonstrate what is my problem you just run the setup.R.
I followed a standard tutorial, but I couldn't find any information of how I can load my BananasCPP class from the Bananas_cpp module inside my Bananas.R wrap function Bananas, while searching for days in the internet. This file does not appear in the namespace of the environment active inside the package, so I think this what should be tackled: "which commands I should add and where to expose my Bananas_cpp module inside the current namespace of the package".
Of course this is a reproducible that I made from a real problem I had.
Thanks a lot for your support!

C++ project with Bazel and GTest

I want to create a Bazel C++ project with gtest for unit tests.
What is the minimal setup?
(I only have Bazel installed on my computer and I am running under Linux)
This is even easier now that googletest provides a BUILD file:
In WORKSPACE
load("#bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "gtest",
remote = "https://github.com/google/googletest",
branch = "v1.10.x",
)
In BUILD
cc_test (
name = "hello_test",
srcs = [
"hello_test.cc",
],
deps = [
"#gtest//:gtest",
"#gtest//:gtest_main" # Only if hello_test.cc has no main()
],
)
The project structure is:
.
├── bin
│   ├── BUILD
│ ├── hello.cpp
├── MyLib
│   ├── BUILD
│ ├── message.hpp
│ ├── message.cpp
│ ├── ...
├── test
│ ├── BUILD
│ ├── message_test.cpp
│ ├── ...
├── gmock.BUILD
└── WORKSPACE
Files related to Bazel+GTest
WORKSPACE
There you download gtest from github:
new_git_repository(
name = "googletest",
build_file = "gmock.BUILD",
remote = "https://github.com/google/googletest",
tag = "release-1.8.0",
)
You define a gmock BUILD file defined below:
gmock.BUILD
This BUILD file is in charge of compiling gtest/gmock:
cc_library(
name = "gtest",
srcs = [
"googletest/src/gtest-all.cc",
"googlemock/src/gmock-all.cc",
],
hdrs = glob([
"**/*.h",
"googletest/src/*.cc",
"googlemock/src/*.cc",
]),
includes = [
"googlemock",
"googletest",
"googletest/include",
"googlemock/include",
],
linkopts = ["-pthread"],
visibility = ["//visibility:public"],
)
cc_library(
name = "gtest_main",
srcs = ["googlemock/src/gmock_main.cc"],
linkopts = ["-pthread"],
visibility = ["//visibility:public"],
deps = [":gtest"],
)
test/BUILD
This build file generate the tests:
cc_test(
name = "MyTest",
srcs = glob(["**/*.cpp"]),
deps = ["//MyLib:MyLib",
"#googletest//:gtest_main"],
)
The test/message_test.cpp file is defined by:
#include "gtest/gtest.h"
#include "MyLib/message.hpp"
TEST(message_test,content)
{
EXPECT_EQ(get_message(),"Hello World!");
}
And that is all! The other files are defined as usual:
Files for the supporting example
MyLib/BUILD
Creates the libMyLib.so and libMyLib.a libraries.
cc_library(
name="MyLib",
hdrs=glob(["**/*.hpp"]),
srcs=glob(["**/*.cpp"]),
visibility = ["//visibility:public"],
)
with a basic message.hpp
#include <string>
std::string get_message();
and message.cpp
#include "MyLib/message.hpp"
std::string get_message()
{
return "Hello World!";
}
example.
bin/BUILD
Creates the hello executable.
cc_binary(
name = "hello",
srcs = ["hello.cpp"],
deps = ["//MyLib:MyLib"],
)
which is:
#include "MyLib/message.hpp"
#include <iostream>
int main()
{
std::cout << "\n" << get_message() << std::endl;
return EXIT_SUCCESS;
}
Usage:
Compiles all targets:
This will also download gtest from its github repo and compile it
bazel build ...
Checks the hello target:
You can run it with:
bazel run bin:hello
Running your tests using GTest
That was the main point of this note:
bazel test ... --test_output=errors
You should get something like:
INFO: Analysed 3 targets (0 packages loaded).
INFO: Found 2 targets and 1 test target...
INFO: Elapsed time: 0.205s, Critical Path: 0.05s
INFO: Build completed successfully, 2 total actions
//test:MyTest
PASSED in 0.0s
Executed 1 out of 1 test: 1 test passes.
Reproduce the results
For your ease I have created a github repo containing this example. I hope it works out of the box.
The current recommended practice is to use http_archive to avoid depending on the system git and take advantage of repository cache.
In WORKSPACE
# 5376968f6948923e2411081fd9372e71a59d8e77 is the commit sha for v1.12.0.
# Periodically update to the latest to "live at head"
http_archive(
name = "com_google_googletest",
sha256 = "199e68f9dff997b30d420bf23cd9a0d3f66bfee4460e2cd95084a2c45ee00f1a",
strip_prefix = "googletest-5376968f6948923e2411081fd9372e71a59d8e77",
urls = ["https://github.com/google/googletest/archive/5376968f6948923e2411081fd9372e71a59d8e77.zip"],
)
In test/BUILD
cc_test(
name = "test_greet",
srcs = ["greeting_test.cpp"],
deps = [
"//src:greeting",
"#com_google_googletest//:gtest_main",
],
)

meteor cucumber test server.call in step definition Error: Method not found [404]

I am new to Meteor Cucumber I am trying to do a basic test
Check a new user
Here is the step definition code
this.Given(/^I am a new user$/, function () {
server.call('fixtures/reset').then(function() {
server.call('fixtures/seedData');
});
});
I get
Error: Method not found [404] ...
at World. (/tests/cucumber/features/step_definitions/static_page.js:17:20)
which points to the first server.call (column 20 is the c of call).
Here is the feature
Given I am a new user
Here is the fixture
(function () {
'use strict';
Meteor.methods({
'fixtures/reset': function () {
Meteor.users.remove({});
},
'fixtures/seedData': function () {
Accounts.createUser({
email: "bob#example.com",
password: "testtest"
});
}
});
})();
Here is the package.js
Package.describe({
name: 'fixtures',
version: '0.0.1',
summary: '',
git: '',
documentation: null,
debugOnly: true
});
Package.onUse(function(api) {
api.versionsFrom('1.2.1');
api.use('ecmascript');
api.addFiles(['fixtures.js'],['server']);
});
I don't understand why this code is failing
It is based on http://www.mhurwi.com/a-basic-cucumber-meteor-tutorial/
Why do you have a package for your tests? That's not necessary. At the root level of your Meteor app folder, make a directory called "tests". My structure looks like this:
tests
└── cucumber
├── features
│   ├── artist
│   │   ├── artist-login.feature
│   │   └── step_definitions
│   │   └── artist-login.js
│   ├── support
│   │   └── hooks.js
│   └── viewer
└── fixtures
└── artist-fixtures.js
You don't need any closures in your fixtures file. This is mine:
Meteor.methods({
'reset': function () {
Meteor.users.remove({});
},
'createTestAccount': function () {
Accounts.createUser({
email: 'test#user.com',
password: 'test123',
profile: {
firstName: 'Test',
lastName: 'User'
}
});
},
'isLoggedIn': function () {
return !!Meteor.userId();
}
});
If you're writing a Meteor package and want to test it, use TinyTest.

How to run multiple Groovy unit tests

I want to keep my groovy source files in their own directory, with the tests being in a separate directory.
I have the directory structure as follows:
.
├── build
│   └── Messenger.class
├── build.xml
├── ivy.xml
├── lib
├── src
│   └── com
│   └── myapp
│   └── Messenger.groovy
└── test
└── unit
├── AnotherTest.groovy
└── MessengerTest.groovy
I can successfully run one test by using the groovy command and specifying the class path for the units under test using -cp to point to build/ but how do I run all the tests in the directory?
Tou can run all unit test with command:
grails test-app unit:
If you have unit, integration, functional... tests you can run all tests with command:
grails test-app
I am new to groovy, but I wrote my own test runner and put it in root directory of my project. Source code:
import groovy.util.GroovyTestSuite
import junit.textui.TestRunner
import junit.framework.TestResult
import static groovy.io.FileType.FILES
public class MyTestRunner {
public static ArrayList getTestFilesPaths(String test_dir) {
// gets list of absolute test file paths
ArrayList testFilesPaths = new ArrayList();
new File(test_dir).eachFileRecurse(FILES) {
if(it.name.endsWith(".groovy")) {
testFilesPaths.add(it.absolutePath)
}
}
return testFilesPaths;
}
public static GroovyTestSuite getTestSuite(ArrayList testFilesPaths) {
// creates test suite using absolute test file paths
GroovyTestSuite suite = new GroovyTestSuite();
testFilesPaths.each {
suite.addTestSuite(suite.compile(it));
}
return suite;
}
public static void runTests(GroovyTestSuite suite) {
// runs test in test suite
TestResult result = TestRunner.run(suite);
// if tests fail return exit code non equal to 0 indicating that
// tests fail it helps if one of your build step is to test files
if (!result.wasSuccessful()) {
System.exit(1);
}
}
}
ArrayList testFilesPaths = MyTestRunner.getTestFilesPaths("tests");
GroovyTestSuite suite = MyTestRunner.getTestSuite(testFilesPaths);
MyTestRunner.runTests(suite)
if you try to use this be aware that if it fails it is most likely that getTestFilesPaths is not working properly.
My directory structure
.
├── test_runner.groovy
├── src
│ └── ...
└── tests
└── Test1.groovy
└── someDir
├── Test2.groovy
└── Test3.groovy
How to run
From the same directory where test_runner.groovy is run:
groovy test_runner.groovy