I'm trying to write some tests for my code. I'm using dependancy injection, and I'm trying to create a faked version of my database to be used when running tests.
I'm using the keyword implements to define my faked database, however I'm getting typescript errors due to the fact that this faked DB is missing certain properties, however, those properties are private, and never used outside the class
Here's an example:
class Database {
private client: MongoClient;
public async getData(query: string): Promise<{ name: string }> {
return await this.client.db('db').collection('collection').findOne({ name: query });
}
}
class MockDatabase implements Database {
public async getData(query: string): Promise<{ name: string }> {
return {
name: 'Jo'
}
}
}
function makeApp(database: Database) {
console.log(`Here's your app`);
}
const fakeDB = new MockDatabase();
const app = makeApp(fakeDB)
Typescript will error both when declaring MockDatabase, as well as when using it in the makeApp function.
Property 'client' is missing in type 'MockDatabase' but required in type 'Database'
How should I approach faking a database or another service like this?
A Database needs to have the client property, and because the property is private, that means you can only get a valid Database from the Database constructor. There is no way to "mock" a Database with a different declaration, because private properties need to come from the same declaration in order to be compatible. This restriction is important, because private properties are not completely inaccessible from outside the object; they are accessible from other instances of the same class. See TypeScript class implements class with private functions for more information.
Anyway, instead of trying to mock a Database, you should consider creating a new interface which is just the "public part" of Database. It would look like this:
// manually written out
interface IDatabase {
getData(query: string): Promise<{ name: string }>
}
You can make the compiler compute this for you, because the keyof operator only returns the public property names of an object type:
// computed
interface IDatabase extends Pick<Database, keyof Database> { }
The type Pick<Database, keyof Database> uses the Pick<T, K> utility type to select just the public properties of Database. In this case that's just "getData", and so the computed IDatabase is equivalent to the manual one.
And now we change references to Database to IDatabase anywhere we only care about the public part:
class MockDatabase implements IDatabase {
public async getData(query: string): Promise<{ name: string }> {
return {
name: 'Jo'
}
}
}
function makeApp(database: IDatabase) {
console.log(`Here's your app`);
}
const fakeDB = new MockDatabase();
const app = makeApp(fakeDB)
And everything works as expected.
Playground link to code
Related
I'm currently going through some tutorial about React Design Patterns, subject: Custom Hooks.
While the concept feels awesome and seems familiar to me, the solution below provided by the tutor made me question how to deal with different data sources.
Is there something like the above mentioned DAO Factory Pattern, you can find in Frameworks like J2E?
Or how is the common approach to handle this challenges in React to make the code maintainable?
My first intension woulda be throwing the stuff into a Factory Component and having implementations for the specific providers, basically like it is shown in the two other Code snippets bewlow.
Is this the regular wayt to do it?
Any help, tips, additional sources to learn this would be highly appreciated.
Here's the solution, with the both possible implementations thrown into the React Form Component:
import axios from 'axios';
import { useDataSource } from './useDataSource';
import { useResource } from './useResource';
import { useUser } from './useUser';
const serverResource = resourceUrl => async () => {
const response = await axios.get(resourceUrl);
return response.data;
};
const localStorageResource = key => () => {
return localStorage.getItem(key);
}
export const UserInfo = ({ userId }) => {
// const user = useResource(`/users/${userId}`);
const user = useDataSource(serverResource(`/users/${userId}`));
const message = useDataSource(localStorageResource('message'));
const { name, age, hairColor, hobbies } = user || {};
return user ? (
<>
<h3>{name}</h3>
<p>Age: {age} years</p>
<p>Hair Color: {hairColor}</p>
<h3>Hobbies:</h3>
<ul>
{hobbies.map(hobby => <li key={hobby}>{hobby}</li>)}
</ul>
</>
) : <p>Loading...</p>;
}
And here's the DAO Factory Pattern example provided by Oracle:
src: https://www.oracle.com/java/technologies/dataaccessobject.html
// Abstract class DAO Factory
public abstract class DAOFactory {
// List of DAO types supported by the factory
public static final int CLOUDSCAPE = 1;
public static final int ORACLE = 2;
public static final int SYBASE = 3;
...
// There will be a method for each DAO that can be
// created. The concrete factories will have to
// implement these methods.
public abstract CustomerDAO getCustomerDAO();
public abstract AccountDAO getAccountDAO();
public abstract OrderDAO getOrderDAO();
...
public static DAOFactory getDAOFactory(
int whichFactory) {
switch (whichFactory) {
case CLOUDSCAPE:
return new CloudscapeDAOFactory();
case ORACLE :
return new OracleDAOFactory();
case SYBASE :
return new SybaseDAOFactory();
...
default :
return null;
}
}
}
...with a concrete implementation:
// Cloudscape concrete DAO Factory implementation
import java.sql.*;
public class CloudscapeDAOFactory extends DAOFactory {
public static final String DRIVER=
"COM.cloudscape.core.RmiJdbcDriver";
public static final String DBURL=
"jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB";
// method to create Cloudscape connections
public static Connection createConnection() {
// Use DRIVER and DBURL to create a connection
// Recommend connection pool implementation/usage
}
public CustomerDAO getCustomerDAO() {
// CloudscapeCustomerDAO implements CustomerDAO
return new CloudscapeCustomerDAO();
}
public AccountDAO getAccountDAO() {
// CloudscapeAccountDAO implements AccountDAO
return new CloudscapeAccountDAO();
}
public OrderDAO getOrderDAO() {
// CloudscapeOrderDAO implements OrderDAO
return new CloudscapeOrderDAO();
}
...
}
how is the common approach to handle this challenges in React to make the code maintainable?
sure you can. As design patterns do not depend on programming language.
If you are using TypeScript, then you can use Abstract class
If you are using plain JavaScript, then you can use the following approach
An example of using Factory in React can be seen here.
I've inherited a typescript react project and I'm unfamiliar with some of the concepts of TS. There is a class being exported like:
export class SomeGateway {
constructor(private url: string){}
public someMethod() {}
}
but whenever i do
const whatever = new SomeGateway('www.google.com');
and try to access whatever.someMethod() it says it doesn't exist, I can see whatever.url though. What's the proper way to access someMethod?
bind the method
export class SomeGateway {
constructor(private url: string){
this.someMethod = thi.someMethod.bind(this);
}
public someMethod() {}
}
I'm trying to move to another database dynamically. I've seen several questions that showed change db files from one to another and they just getting some information from next database. But what I need is completely moving to second database. How should I do this? I've seen that in order to achieve this dsn (in db.php file) should be altered. But I changed it and it's still not changed?? I should have full access to second database closing first one. Give me advice please
Configs like db.php are not intended to be changed in process (while PHP is processing). They are loaded once in the initialization, when request is entered the framework.
As an alternative, you can configure second DB beforehand in db.php, and change between them dynamically like:
Yii::$app->db // your default Database
and
Yii::$app->db2 // Second configured Database, to which you can switch dynamically later
You can learn about multiple database connections here
So, if you want ActiveRecord(for instance User) to be able to access two databases, you can define some static variable, which specifies from which DB to read/write. For example:
class User extends \yii\db\ActiveRecord
{
const DB_DATABASE1 = 'db1';
const DB_DATABASE2 = 'db2';
private static $db = self::DB_DATABASE1;
public static function setDb($db)
{
self::$db = $db;
}
public static function getDb()
{
switch (self::$db) {
case self::DB_DATABASE1:
return Yii::$app->db;
case self::DB_DATABASE2:
return Yii::$app->db2;
default:
throw new \Exception("Database is not selected");
}
}
//...
And then use it in Controller like:
User::setDb(User::DB_DATABASE1);
$usersDB1 = User::find()->all();
User::setDb(User::DB_DATABASE2);
$usersDB2 = User::find()->all();
Set up multiple database connections in your main.php or main-local.php configuration
Yii::$app->db1;
Yii::$app->db2;
Yii::$app->db3;
when making inquiries
$usersDB1=User::find()->all(Yii::$app->db1);
$usersDB2=User::find()->all(Yii::$app->db2);
$usersDB3=User::find()->all(Yii::$app->db3);
Globally switch to a different database dynamically
I have combined Yerke's answer above and Write & use a custom Component in Yii2.0 here.
First, create a component class to do the DB switching. Here, I call it DbSelector and put it in app\components\DbSelector.php.
namespace app\components;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
class DbSelector extends Component {
const DB_MAIN = 'db';
const DB_SUB1 = 'db1';
const DB_SUB2 = 'db2';
private static $db = self::DB_MAIN;
public static function setDb($db)
{
self::$db = $db;
}
public static function getDb()
{
return \Yii::$app->get(self::$db);
}
}
Second, modify the config/web.php file or whichever your config file is to have multiple databases and add DbSelector as a Yii::$app component.
$config = [
'components' => [
//...
'db' => $db,
'db1' => $db1,
'db2' => $db2,
'dbSelector' => [
'class' => 'app\components\DbSelector',
],
//...
],
//...
];
Third, in each model class, add the following static function getDb() to call the DbSelector getDb():
public static function getDb()
{
return \Yii::$app->dbSelector->getDb();
}
For example,
class DiningTable extends \yii\db\ActiveRecord
{
public static function tableName()
{
return '{{%dining_table}}';
}
public static function getDb()
{
return \Yii::$app->dbSelector->getDb();
}
//...
}
class Customer extends \yii\db\ActiveRecord
{
public static function tableName()
{
return '{{%customer}}';
}
public static function getDb()
{
return \Yii::$app->dbSelector->getDb();
}
//...
}
Lastly, to globally switch to a different database, use \Yii::$app->dbSelector->setDb(YOUR_PREFERRED_DB),
use app\components\DbSelector;
//...
\Yii::$app->dbSelector->setDb(DbSelector::DB_SUB1);
$tables_1 = DiningTable::findAll();
$customers_1 = Customer::find()->where(['<', 'dob', '2000-01-01'])->all();
\Yii::$app->dbSelector->setDb(DbSelector::DB_MAIN);
$tables_main = DiningTable::findAll();
$customers_main = Customer::find()->where(['<', 'dob', '2000-01-01'])->all();
Using rxjs with Angular 2 RC.1 and I'm creating a few Observer's. I have the following in a service:
taxRates$: Observable<TaxRate[]>;
taxTables$: Observable<TaxTable[]>;
private _taxRatesObserver: Observer<TaxRate[]>;
private _taxTablesObserver: Observer<TaxTable[]>;
constructor(private http: HttpService) {
this.taxRates$ = new Observable<TaxRate[]>(observer =>
this._taxRatesObserver = observer
).share();
this.taxTables$ = new Observable<TaxTable[]>(observer =>
this._taxTablesObserver = observer
).share();
// more stuff here
}
The _taxRatesObserver is created correctly, but _taxTablesObserver is not. When I inspect the dev tools, I see this:
_taxRatesObserver:Subscriber
http:HttpService
taxRates$:RefCountObservable
taxTables$:RefCountObservable
I'd expect to also see _taxTablesObserver:Subscriber but if I log it via the console, it always shows as undefined. TaxTable, if it matters, looks like this:
export class TaxTable {
constructor(
public id: number,
public name: string,
public description: string) {}
}
From docs:
subscribe the function that is called when the Observable is initially subscribed to.
_taxRatesObserver and _taxTablesObserver are initialized after subscription to taxRates$ and taxTables$ correspondingly.
Most probably taxTables$ does not have subscribers.
I currently implemented an angular $http interceptor adding custom headers to requests based on a localstorage value (I need to implement a "Su" feature in my app)
I need to "deactivate" this behaviour on some special requests (=I need to be able to configure this on a per-request basis), and I'd like to do this by putting an extra config parameter denoting this when calling my $http methods.
The interceptor is looking like this :
$httpProvider.interceptors.push((localStorageService: ng.local.storage.ILocalStorageService) => {
return {
request: (config: ng.IRequestShortcutConfig) => {
var su = localStorageService.get<string>('Su');
if(su && !("avoidSudo" in config)){
config.headers.Su = `{ "principal": "${su}" }`;
}
return config;
}
}
});
And the $http service call is looking like this when I want to deactivate my "su" feature :
this.$http.get('/api/sessions/current', { avoidSudo: true })
In typescript 1.6, this code doesn't compile as $http.get()'s second argument is expected to be a ng.IRequestShortcutConfig which obviously doesn't contain my specific avoidSudo field (Typescript compilation error is perfectly right here)
I can workaround the compilation error by replacing the { avoidSudo: true } by <any>{ avoidSudo: true } but this is clearly not ideal in terms of typesafety
I tried to create a new SkipSudoRequestShortcutConfig dedicated class (implementing ng.IRequestShortcutConfig) for this purpose. Something like this :
module mymodule {
export class SkipSudoRequestShortcutConfig implements ng.IRequestShortcutConfig {
// For testing purposes only
_noSudo: boolean;
constructor(
public params?: any,
public headers?: any,
public xsrfHeaderName?: string,
public xsrfCookieName?: string,
public cache?: any,
public withCredentials?: boolean,
public data?: any,
public transformRequest?: any,
public transformResponse?: any,
public timeout?: any,
public responseType?: string
){
this._noSudo = true;
}
}
}
called like this :
var config = new SkipSudoRequestShortcutConfig();
console.log(config instanceof SkipSudoRequestShortcutConfig); // Shows true
console.log(config._noSudo); // Shows true
this.$http.get('/api/sessions/current', config)
and used like this in the interceptor :
request: (config: ng.IRequestShortcutConfig) => {
var su = localStorageService.get<string>('Su');
console.log(config instanceof SkipSudoRequestShortcutConfig); // Shows *false* :(
// console.log(config._noSudo); // Doesn't compile, but if executed at runtime with a breakpoint, it works and display true
if(su && !(config instanceof mymodule.SkipSudoRequestShortcutConfig)){
config.headers.Su = `{ "principal": "${su}" }`;
}
return config;
}
but once in the request handler, the instanceof test was falsy.
I was wondering what would be the best/simplest way to achieve this goal.
Do you think the ng.IRequestShortcutConfig is missing a special config field allowing to put custom fields from $http invocations to interceptor handlers ?
Thanks in advance,
It's important to remember that types in TypeScript are just "type helpers" and removed when transpiled to javascript. So you don't need to implement a new $http service. Instead you can just create a new type that suites your needs.
In reality it's because the angular type definition is lacking.
You can fix this by creating the following interfaces.
export interface IRequestShortcutConfigWithCustomConfig extends ng.IRequestShortcutConfig {
[key: string]: any;
}
I'm using a dictionary style type cause that'll support everything. But you could change that to avoidSudo: boolean; if you don't want a general definition.
export interface IHttpServiceWithCustomConfig extends ng.IHttpService {
get<T>(url: string, config?: IRequestShortcutConfigWithCustomConfig): ng.IHttpPromise<T>;
}
Then in your controller you just use the new interface.
constructor(private $http: IHttpServiceWithCustomConfig) {
$http.get("/api/sessions/current", { avoidSudo: true }
}
You do the exact same with IHttpInterceptor
export interface IRequestConfigWithCustomConfig extends ng.IRequestConfig, IRequestShortcutConfigWithCustomConfig {
}
export interface IHttpInterceptorWithCustomConfig extends ng.IHttpInterceptor {
request?: (config: IRequestConfigWithCustomConfig) => IRequestConfigWithCustomConfig | ng.IPromise<IRequestConfigWithCustomConfig>;
}