[](https://coveralls.io/github/morten-olsen/abtesting?branch=test) [](https://
npm install labyrinth-guards 
* Implement automatic impression tracking
* Finalize tooling
* Find a better name
For a minimal setup six elements are needed: ABRoot, withABTest and VariantA/VariantB along with an ABManger with an ABTestSuite and an ABUser
A.jsx
``javascript
import React, { Component } from 'react';
import { withABTest } from from 'abstesting';
class A extends Component {
handleClick() {
this.props.trackConversion();
}
render() {
return (
export default withABTest(A);
`
B.jsx
`javascript
import React, { Component } from 'react';
import { withABTest } from from 'abstesting';
class B extends Component {
handleClick() {
this.props.trackConversion();
}
render() {
return (
export default withABTest(B);
`
App.jsx
`javascript
import React from 'react';
import { VariantA, VariantB } from 'abtesting';
import A from './A';
import B from './B';
export default () => (
test1.js
`javascript
import { FunctionTest } from 'abtesting';export default new FunctionTest(
(userInfo) => userInfo.age > 15,
(userInfo) => userInfo.id % 2,
);
`index.jsx
`javascript
import React from 'react';
import ReactDOM from 'react-dom';
import { ABRoot, ABUser, ABTestSuite, ABManager } from 'abtesting';
import test1 from './tests';
import App from './App.js';const manager = new ABManager({
user: new ABUser(),
testSuite = new TestSuite(),
});
manager.setUserInfo({
id: 1234,
age: 23,
});
manager.setTests({
test1,
});
ReactDOM.render(
,
document.body,
)
`
Using different components
Another usefull feature is to be able to serve different components depending on which test bucket the user is in. This can be done using the
createABTest higher order functionA.jsx
`javascript
export default ({value}) => VariantA {value}
`B.jsx
`javascript
export default ({value}) => VariantB {value}
`Component.jsx
`javascript
import { createABSplit } from 'abtesting';
import A from './A';
import B from './B';export default createABSplit({
name: 'test1',
VariantA: A,
VariantB: B,
})
`Now when rendering
, the user which are presented the original version will see "VariantA hello" and user presented with the alternative version will see "VariantB hello".User information
In order to figure out if users is in a given group, a set of user trades need to be provided. The test split is stateless, so therefore it can not store any information about previous a/b buckets a user has fallen into, and therefor this needs to be something which can be figured out based on values about the current user.
These are set by calling
setUserInfo on the manager object, which was passed to ABRoot. When these values change, all A/B splits, currently presented to the user will be re-evaluated, and if a user changes bucket, these changes are reflected.`javascript
import { ABManager, ABUser, ABRoot } from 'abtesting';const manager = new ABManager({
...
user: new ABUser();
});
manager.setInfo({
age: 23
});
const root = (
)
`Tests
$3
$3
`javascript
import { ABTest } from 'abstesting';
class CustomTest extends ABTest {
isIncludedInTest(userInfo) {
return userInfo.isLoggedIn;
} isInTestGroup(userInfo) {
return userInfo.id % 2;
}
}
manager.setTest({
someTest: new CustomTest(),
});
`Services
The component comes with a few different service types preloaded and provides the tools for creating custom service
$3
`javascript
import { ABService, ABManager } from 'abstesting';class CustomService extends ABService {
constructor(endpoint) {
super();
this.endpoint = endpoint;
}
async updateTests() {
const response = await fetch(endpoint).then(res => res.json());
return response;
}
async sendData(data) {
const data = new FormData();
data.append( "json", JSON.stringify(data) );
return fetch(endpoint, {
method: "POST",
body: data
});
}
async trackImpression(name, type) {
await this.sendData({
type: 'impression',
name,
type,
});
}
async trackConversion(name, type, params) {
await this.sendData({
type: 'impression',
name,
type,
});
}
}
const manager = new ABManager {
...
service: new CustomService(),
}
const root = (
)
``