Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: move closer to test double definitions #150

Merged
merged 38 commits into from
Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e610abc
fix: Mocking a class with getters doesnt set default initialised value
ebebbington Apr 9, 2022
2c48237
fix tests
ebebbington Apr 10, 2022
5111da2
fmt
ebebbington Apr 10, 2022
a8df3f2
test fixes (#149)
crookse Apr 10, 2022
289c455
feat(mock): add mock.method(...).willReturn(...);
crookse Apr 10, 2022
caf0ea8
chore: cleaning up
crookse Apr 11, 2022
62fed94
feat: add mock.method(...).willThrow(...)
crookse Apr 11, 2022
fd258ac
test: mock.method().willThrow() and mock.method().willReturn()
crookse Apr 11, 2022
df28899
feat: add mixin for mock
crookse Apr 11, 2022
d5833f1
fix: issue with redefining getters and setters
crookse Apr 11, 2022
73996ee
test: testing current implementations thus far
crookse Apr 11, 2022
c2fa3de
Merge branch 'feat/mock-as-mixin' into feat/configurable-methods-spie…
crookse Apr 11, 2022
3ee8e39
chore: deno fmt
crookse Apr 11, 2022
6a1e98b
Merge branch 'fix-getters' into feat/new-mock-methods
crookse Apr 11, 2022
6299ba2
feat: add dummy logic
crookse Apr 12, 2022
8f5f5d9
chore: clean up
crookse Apr 12, 2022
825748b
chore: move mock logic into mock folder
crookse Apr 12, 2022
0689930
feat: fakes
crookse Apr 12, 2022
dc303a5
Merge branch 'feat/dummies' into feat/new-mock-methods
crookse Apr 12, 2022
647bd2d
chore: deno fmt
crookse Apr 12, 2022
0cb4c75
chore: fix doc block
crookse Apr 12, 2022
937c23f
feat: add method expectation calls for mocks
crookse Apr 12, 2022
3d00a25
chore: fix param name
crookse Apr 12, 2022
b45e231
test: add test creating dummy without constructor args
crookse Apr 12, 2022
4d05200
chore: make setter adjacent to getter
crookse Apr 12, 2022
37cacc0
chore: make file marker comment bigger
crookse Apr 12, 2022
49e9fbe
refactor!: change stub to only stub data members
crookse Apr 12, 2022
5cfcaab
feat: new readme
crookse Apr 12, 2022
05c66d2
docs: update README to state Gerard Meszaros
crookse Apr 12, 2022
15dd9cd
chore: state Gerard Meszaros in doc blocks
crookse Apr 12, 2022
b1e4c77
fix: accidentally removed Constructor type import
crookse Apr 12, 2022
cf1297f
chore: deno fmt and clean up
crookse Apr 12, 2022
1e9d2c5
refactor!: shorten syntax to stub class properties and methods; make …
crookse Apr 12, 2022
50e9dc5
chore: cleaning up
crookse Apr 13, 2022
ca2b88e
chore: remove other mock test file
crookse Apr 13, 2022
cc0ebdf
fix: change README title to Rhum
crookse Apr 13, 2022
92bd978
chore: remove playground files
crookse Apr 13, 2022
08e47ef
chore: remove constructor comment; move init method down
crookse Apr 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 12 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,17 @@
<p align="center">
<img src="./logo.svg" height="250" alt="Rhum - A lightweight testing framework for Deno.">
<h1 align="center">Rhum</h1>
</p>
<p align="center">A test double module to stub and mock your code</p>
<p align="center">
<a href="https://github.com/drashland/rhum/releases">
<img src="https://img.shields.io/github/release/drashland/rhum.svg?color=bright_green&label=latest">
</a>
<a href="https://github.com/drashland/rhum/actions">
<img src="https://img.shields.io/github/workflow/status/drashland/rhum/master?label=ci">
</a>
<a href="https://discord.gg/SgejNXq">
<img src="https://img.shields.io/badge/chat-on%20discord-blue">
</a>
<a href="https://twitter.com/drash_land">
<img src="https://img.shields.io/twitter/url?label=%40drash_land&style=social&url=https%3A%2F%2Ftwitter.com%2Fdrash_land">
</a>
<a href="https://www.youtube.com/watch?v=WhG5hLrcaVQ&list=PLlFUbR9MhiNU9VlCi97JkahXyDYcL_vUz&ab_channel=drashland">
<img src="https://img.shields.io/badge/Tutorials-YouTube-red">
</a>
</p>
# Rhum

---
[![Latest Release](https://img.shields.io/github/release/drashland/rhum.svg?color=bright_green&label=latest)](#)
[![CI master](https://img.shields.io/github/workflow/status/drashland/rhum/master?label=ci%20-%20master)](#)
[![YouTube](https://img.shields.io/badge/tutorials-youtube-red)](https://rb.gy/vxmeed)

### Features
<img align="right" height="100" src="./logo.svg" alt="Rhum logo">

- Lightweight
- Zero 3rd party dependencies
- Simple and easy to use
- Asynchronous support
- Mock requests
- Hooks
Rhum is a test double library that follows
[test double definitions](https://martinfowler.com/bliki/TestDouble.html) from
Gerard Meszaros.

### Getting Started
View the full documentation at https://drash.land/rhum.

To add Rhum to your project, follow the quick start guide
[here](https://drash.land/rhum/#/#quickstart).

## Contributing

Want to contribute? Follow the Contributing Guidelines
[here](https://github.com/drashland/.github/blob/master/CONTRIBUTING.md).

## License

All code is released under the [MIT License](./LICENSE).
In the event the documentation pages are not accessible, please view the raw
version of the documentation at
https://github.com/drashland/website-v2/tree/main/docs.
153 changes: 107 additions & 46 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,124 @@
import type { Constructor, Stubbed } from "./src/types.ts";
import { MockBuilder } from "./src/mock_builder.ts";

export type { Constructor, Stubbed } from "./src/types.ts";
export { MockBuilder } from "./src/mock_builder.ts";
import type { Constructor, StubReturnValue } from "./src/types.ts";
import { MockBuilder } from "./src/mock/mock_builder.ts";
import { FakeBuilder } from "./src/fake/fake_builder.ts";
export * as Types from "./src/types.ts";
export * as Interfaces from "./src/interfaces.ts";

/**
* Get the mock builder to mock classes.
* Create a dummy.
*
* @param constructorFn - The constructor function of the object to mock.
* Per Martin Fowler (based on Gerard Meszaros), "Dummy objects are passed
* around but never actually used. Usually they are just used to fill parameter
* lists."
*
* @param constructorFn - The constructor function to use to become the
* prototype of the dummy. Dummy objects should be the same instance as what
* they are standing in for. For example, if a `SomeClass` parameter needs to be
* filled with a dummy because it is out of scope for a test, then the dummy
* should be an instance of `SomeClass`.
* @returns A dummy object being an instance of the given constructor function.
*/
export function Dummy<T>(constructorFn?: Constructor<T>): T {
const dummy = Object.create({});
Object.setPrototypeOf(dummy, constructorFn ?? Object);
return dummy;
}

/**
* Get the builder to create fake objects.
*
* Returns an instance of the MockBuilder class.
* Per Martin Fowler (based on Gerard Meszaros), "Fake objects actually have
* working implementations, but usually take some shortcut which makes them not
* suitable for production (an InMemoryTestDatabase is a good example)."
*
* class ToBeMocked { ... }
* @param constructorFn - The constructor function of the object to fake.
*
* const mock = Rhum
* .mock(ToBeMocked)
* .withConstructorArgs("someArg") // if the class to be mocked has a constructor and it requires args
* .create();
* @returns Instance of `FakeBuilder`.
*/
export const Mock = <T>(constructorFn: Constructor<T>): MockBuilder<T> => {
return new MockBuilder(constructorFn);
};
export function Fake<T>(constructorFn: Constructor<T>): FakeBuilder<T> {
return new FakeBuilder(constructorFn);
}

/**
* Stub a member of an object.
* Get the builder to create mocked objects.
*
* @param obj -The object containing the member to stub.
* @param member -The member to stub.
* @param value - The return value of the stubbed member.
* Per Martin Fowler (based on Gerard Meszaros), "Mocks are pre-programmed with
* expectations which form a specification of the calls they are expected to
* receive. They can throw an exception if they receive a call they don't expect
* and are checked during verification to ensure they got all the calls they
* were expecting."
*
* Returns the object in question as a Stubbed type. Being a Stubbed type
* means it has access to a `.stub()` method for stubbing properties and
* methods.
* @param constructorFn - The constructor function of the object to mock.
*
* class MyObject {
* public some_property = "someValue";
* }
* @returns Instance of `MockBuilder`.
*/
export function Mock<T>(constructorFn: Constructor<T>): MockBuilder<T> {
return new MockBuilder(constructorFn);
}

/**
* Create a stub function that returns "stubbed".
*/
export function Stub<T, R>(): () => "stubbed";
/**
* Take the given object and stub its given data member to return the given
* return value.
*
* // Define the object that will have stubbed members as a stubbed object
* const myStubbedObject = Rhum.stubbed(new MyObject());
* @param obj - The object receiving the stub.
* @param dataMember - The data member on the object to be stubbed.
* @param returnValue - (optional) What the stub should return. Defaults to
* "stubbed".
*/
export function Stub<T, R>(
obj: T,
dataMember: keyof T,
returnValue?: R,
): StubReturnValue<T, R>;
/**
* Take the given object and stub its given data member to return the given
* return value.
*
* // Stub the object's some_property property to a certain value
* myStubbedObject.stub("some_property", "this property is now stubbed");
* Per Martin Fowler (based on Gerard Meszaros), "Stubs provide canned answers
* to calls made during the test, usually not responding at all to anything
* outside what's programmed in for the test."
*
* // Assert that the property was stubbed
* Rhum.asserts.assertEquals(myStubbedObject.some_property, "this property is now stubbed");
* @param obj - (optional) The object receiving the stub. Defaults to a stub
* function.
* @param dataMember - (optional) The data member on the object to be stubbed.
* Only used if `obj` is an object.
* @param returnValue - (optional) What the stub should return. Defaults to
* "stubbed" for class properties and a function that returns "stubbed" for
* class methods. Only used if `object` is an object and `dataMember` is a
* member of that object.
*/
export const Stub = <T>(obj: T): Stubbed<T> => {
(obj as unknown as { [key: string]: boolean }).is_stubbed = true;
(obj as unknown as {
[key: string]: (property: string, value: unknown) => void;
}).stub = function (
property: string,
value: unknown,
): void {
Object.defineProperty(obj, property, {
value: value,
});
};
export function Stub<T, R>(
obj?: T,
dataMember?: keyof T,
returnValue?: R,
): unknown {
if (obj === undefined) {
return function stubbed() {
return "stubbed";
};
}

return obj as Stubbed<T>;
};
// If we get here, then we know for a fact that we are stubbing object
// properties. Also, we do not care if `returnValue` was passed in here. If it
// is not passed in, then `returnValue` defaults to "stubbed". Otherwise, use
// the value of `returnValue`.
if (typeof obj === "object" && dataMember !== undefined) {
// If we are stubbing a method, then make sure the method is still callable
if (typeof obj[dataMember] === "function") {
Object.defineProperty(obj, dataMember, {
value: () => returnValue !== undefined ? returnValue : "stubbed",
writable: true,
});
} else {
// If we are stubbing a property, then just reassign the property
Object.defineProperty(obj, dataMember, {
value: returnValue !== undefined ? returnValue : "stubbed",
writable: true,
});
}
}
}
Loading