Skip to content

Instantly share code, notes, and snippets.

@JoshuaKGoldberg
Last active May 25, 2018 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshuaKGoldberg/f1c8e4dbc0c02cbdafa437ebe20289e3 to your computer and use it in GitHub Desktop.
Save JoshuaKGoldberg/f1c8e4dbc0c02cbdafa437ebe20289e3 to your computer and use it in GitHub Desktop.
πŸƒ Unit Tests: SOLID, Modern, Electric Boogaloo πŸƒ
export const createRouteDetails = (lookupId?: string): IRouteDetails => {
if (lookupId === undefined) {
return {
pageMode: PageMode.MySways,
};
}
return {
lookupId,
pageMode: PageMode.Document,
};
};
describe("DialogManager", () => {
describe("showMessage", () => {
it("sets the current dialog message", () => {
// Arrange
const manager = new DialogManager("", new Map());
const message = "foo";
// Act
manager.showMessage(message);
// Assert
expect(manager.getDialog()).toEqual(message);
});
it("uses the default message when no message is provided", () => {
// Arrange
const defaultMessage = "foo";
const manager = new DialogManager(defaultMessage, new Map());
// Act
manager.showMessage(undefined);
// Assert
expect(manager.getDialog()).toEqual(defaultMessage);
});
it("uses a message alias when the message matches an alias", () => {
// Arrange
const alias: [string, string] = ["foo", "bar"];
const manager = new DialogManager("", new Map([alias]));
// Act
manager.showMessage(alias[0]);
// Assert
expect(manager.getDialog()).toEqual(alias[1]);
});
it("uses the message when it's provided and doesn't match any alias", () => {
// Arrange
const manager = new DialogManager("", new Map());
const message = "foo";
// Act
manager.showMessage(message);
// Assert
expect(manager.getDialog()).toEqual(message);
});
});
});
class DialogManager {
private currentDialog?: string;
private readonly defaultMessage: string;
private readonly messageAliases: Map<string, string>
public constructor(defaultMessage: string, messageAliases: Map<string, string>) {
this.defaultMessage = defaultMessage;
this.messageAliases = messageAliases;
}
public getDialog(): string | undefined {
return this.currentDialog;
}
public showMessage(message: string | undefined): void {
this.currentDialog = this.createDialogMessage(message);
}
private createDialogMessage(message: string | undefined): string {
if (message === undefined) {
return this.defaultMessage;
}
const messageAlias = this.messageAliases.get(message);
return messageAlias === undefined
? messageAlias
: message;
}
}
describe("DialogMessageFactory", () => {
describe("createDialogMessage", () => {
it("uses the default message when no message is provided", () => {
// Arrange
const defaultMessage = "foo";
const manager = new DialogMessageFactory(defaultMessage, new Map());
// Act
manager.createDialogMessage(undefined);
// Assert
expect(manager.getDialog()).toEqual(defaultMessage);
});
it("uses a message alias when the message matches an alias", () => {
// Arrange
const alias: [string, string] = ["foo", "bar"];
const manager = new DialogMessageFactory("", new Map([alias]));
// Act
manager.createDialogMessage(alias[0]);
// Assert
expect(manager.getDialog()).toEqual(alias[1]);
});
it("uses the message when it's provided and doesn't match any alias", () => {
// Arrange
const manager = new DialogMessageFactory("", new Map());
const message = "foo";
// Act
manager.createDialogMessage(message);
// Assert
expect(manager.getDialog()).toEqual(message);
});
});
});
interface IDialogMessageFactory {
createDialogMessage(message: string | undefined): string;
}
class DialogMessageFactory implements IDialogMessageFactory {
private readonly defaultMessage: string;
private readonly messageAliases: Map<string, string>
public constructor(defaultMessage: string, messageAliases: Map<string, string>) {
this.defaultMessage = defaultMessage;
this.messageAliases = messageAliases;
}
public createDialogMessage(message: string | undefined) {
if (message === undefined) {
return this.defaultMessage;
}
const messageAlias = this.messageAliases.get(message);
return messageAlias === undefined
? messageAlias
: message;
}
}
describe("DialogStore", () => {
describe("getDialog", () => {
it("has an undefined dialog when no message has been set yet", () => {
// Arrange
const dialogStore = new DialogStore({
(message: string) => message,
});
// Act
const dialog = dialogStore.getDialog();
// Assert
expect(dialog).toBeUndefined();
});
});
describe("showMessage", () => {
it("creates a dialog with a generated message when given one", () => {
// Arrange
const dialogStore = new DialogStore({
(message: string) => `Message: ${message}`,
});
// Act
dialogStore.showMessage("foo");
// Assert
expect(dialogStore.getDialog()).toBeUndefined();
});
});
});
class DialogStore {
private currentDialog?: string;
private readonly dialogMessageFactory: IDialogMessageFactory;
public constructor(dialogMessageFactory: IDialogMessageFactory) {
this.dialogMessageFactory = dialogMessageFactory;
}
public getDialog(): string | undefined {
return this.currentDialog;
}
public showMessage(message: string | undefined): void {
this.currentDialog = this.dialogMessageFactory.createDialogMessage(message);
}
}
describe("FooProcessor", () => {
describe("process", () => {
it("number multiplication", () => {
const processor = new FooProcessor();
const fromPositive = processor.process(2);
expect(fromPositive).toEqual(4);
const fromNegative = processor.process(-2);
expect(fromNegative).toEqual(2);
});
});
});
describe("FooProcessor", () => {
describe("process", () => {
it("returns squared input when the input is positive", () => {
// Arrange
const processor = new FooProcessor();
// Act
const fromPositive = processor.process(2);
// Assert
expect(fromPositive).toEqual(4);
});
it("returns absolute input when the input is negative", () => {
// Arrange
const processor = new FooProcessor();
// Act
const fromNegative = processor.process(-2);
// Assert
expect(fromNegative).toEqual(2);
});
});
});
// Jest describe, which declares a group of tests
describe("Image", () => {
// Jest it, which declares a single test
it("calls the store's onClick when the image is clicked (expanded)", () => {
// Arrange
// Jasmine spy that stores calls given to it
const spy = jasmine.createSpy();
// (backing store for the image)
const store = new ImageStore(spy, "");
// React component shallow-rendered by Enzyme
const component = shallow(<Image store={store} />);
// Act
// Use Enzyme's API hooks to:
component
.find("img") // Find the simulated <img ... /> (like using jQuery or document.querySelector)
.simulate("click") // Simulate a user clicking the img
;
// Assert
// Jasmine assertion that the spy should have been called once
expect(spy).toHaveBeenCalledTimes(1);
});
});
describe("Image", () => {
it("calls the store's onClick when the image is clicked", () => {
// Arrange
const spy = jasmine.createSpy();
const imageStore = new ImageStore(spy, "");
const component = shallow(<Image store={imageStore} />);
// Act
rendered.find("img").simulate("click");
// Assert
expect(spy).toHaveBeenCalledTimes(1);
});
});
export const Image = observer(({ store }: IStoreProps<ImageStore>) => (
<img onClick={store.onClick} src={store.src} />
));
class ImageStore {
public constructor(
public readonly onClick: () => void,
public readonly src: string,
) { }
}
it("creates details with the Document page mode when given a lookupId", () => {
// Arrange
const lookupId = "lookupId";
// Act
const routeDetails = createRouteDetails(lookupId);
// Assert
expect(routeDetails).toEqual({
lookupId,
pageMode: PageMode.Document,
});
});
it("creates details with the My Sways page mode when given an undefined lookupId", () => {
// Act
const routeDetails = createRouteDetails(undefined);
// Assert
expect(routeDetails).toEqual({
pageMode: PageMode.MySways,
});
});
describe("FooProcessor", () => {
describe("process", () => {
it("generates a positive square when given a positive number", () => {
// Arrange
const processor = new FooProcessor();
// Act
const result = processor.process(2);
// Assert
expect(result).toEqual(4);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment